sw/qa/extras/layout/data/tdf161508.fodt | 54 ++++++++++++++++++++++++++++++++ sw/qa/extras/layout/layout3.cxx | 10 +++++ sw/source/core/layout/tabfrm.cxx | 28 ++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-)
New commits: commit 527bde6d02e49bbbe5a81a126d65947c527bd58f Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Tue Jun 11 17:39:26 2024 +0500 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Jun 14 10:53:51 2024 +0200 tdf#161508: add another loop control hack It beats me how to resolve the oscillation cleanly, when in a row split mode, lcl_CalcMinRowHeight calculates a small value, but in a non-split mode, it returns a larger value (which is expected), and that resulted in recalc, getting stuck forever in the nested SwTabFrame::MakeAll. So this puts an oscillation control here. The placement is mostly heuristical. It is hackish also in the sense that it only checks the frame size and position, but ignores the state; so it might turn out that it returns too early, when a different combination of flags was about to be attempted. The unit test tests two things: 1. The main aspect of freeze; 2. The expected correct layout. If (when) the hack turns out problematic, and its fix happens to break the second part of the test, that is unfortunate, but the most important thing is to keep it from hanging. Change-Id: If31d8527b4677b5211dcd3308578118c7066d68c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168677 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> (cherry picked from commit 0c49aa58cfbb81073e34b1d47861a5a1fdd44114) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168627 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168773 Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/extras/layout/data/tdf161508.fodt b/sw/qa/extras/layout/data/tdf161508.fodt new file mode 100644 index 000000000000..31c150ff14cf --- /dev/null +++ b/sw/qa/extras/layout/data/tdf161508.fodt @@ -0,0 +1,54 @@ +<?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 Sans" svg:font-family="'Liberation Sans'" 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"/> + <style:text-properties style:font-name="Liberation Sans" fo:font-size="11pt"/> + </style:default-style> + </office:styles> + <office:automatic-styles> + <style:style style:name="R1" style:family="table-row"> + <style:table-row-properties style:min-row-height="7mm" fo:keep-together="always"/><!-- --> + </style:style> + <style:style style:name="C1" style:family="table-cell"> + <style:table-cell-properties fo:padding-top="0" fo:padding-bottom="0" fo:border="0.5pt solid #000000"/> + </style:style> + <style:style style:name="P1" style:family="paragraph"> + <style:paragraph-properties fo:margin-bottom="28mm"/> + </style:style> + <style:page-layout style:name="pm1"> + <style:page-layout-properties fo:page-width="9cm" fo:page-height="5cm" style:print-orientation="portrait" fo:margin-top="8mm" fo:margin-bottom="8mm" fo:margin-left="8mm" fo:margin-right="8mm"/> + </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">Hangs since LO5.3</text:p> + <table:table> + <table:table-column/> + <table:table-row> + <table:table-cell> + <table:table> + <table:table-column table:number-columns-repeated="2"/> + <table:table-row table:style-name="R1"> + <table:table-cell table:style-name="C1" table:number-rows-spanned="2"/><!-- --> + <table:table-cell table:style-name="C1"/> + </table:table-row> + <table:table-row table:style-name="R1"> + <table:covered-table-cell/><!-- --> + <table:table-cell table:style-name="C1"/> + </table:table-row> + </table:table> + </table:table-cell> + </table:table-row> + </table:table> + <text:p/> + </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 f931de6d2173..61628bc27a4d 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -2536,6 +2536,16 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf160958_orphans) assertXPath(pExportDump, "//page[2]/body/txt[1]/SwParaPortion/SwLineLayout"_ostr, 1); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, TestTdf161508) +{ + // This document must not hang on load. + createSwDoc("tdf161508.fodt"); + auto pExportDump = parseLayoutDump(); + // The table must move completely to the second page + assertXPath(pExportDump, "//page[1]/body/tab"_ostr, 0); + assertXPath(pExportDump, "//page[2]/body/tab"_ostr, 1); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index 13e212f6ea16..39d936cf7a23 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -2047,6 +2047,29 @@ namespace { return bRet; } + +// Similar to SwObjPosOscillationControl in sw/source/core/layout/anchoreddrawobject.cxx +class PosSizeOscillationControl +{ +public: + bool OscillationDetected(const SwFrameAreaDefinition& rFrameArea); + +private: + std::vector<std::pair<SwRect, SwRect>> maFrameDatas; +}; + +bool PosSizeOscillationControl::OscillationDetected(const SwFrameAreaDefinition& rFrameArea) +{ + if (maFrameDatas.size() == 20) // stack is full -> oscillation + return true; + + for (const auto& [area, printArea] : maFrameDatas) + if (rFrameArea.getFrameArea() == area && rFrameArea.getFramePrintArea() == printArea) + return true; + + maFrameDatas.emplace_back(rFrameArea.getFrameArea(), rFrameArea.getFramePrintArea()); + return false; +} } // extern because static can't be friend @@ -2244,6 +2267,7 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) int nUnSplitted = 5; // Just another loop control :-( int nThrowAwayValidLayoutLimit = 5; // And another one :-( + PosSizeOscillationControl posSizeOscillationControl; // And yet another one. SwRectFnSet aRectFnSet(this); while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() ) { @@ -2934,7 +2958,9 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) // split operation as good as possible. Therefore we // do some more calculations. Note: Restricting this // to nDeadLine may not be enough. - if ( bSplitError && bTryToSplit ) // no restart if we did not try to split: i72847, i79426 + // tdf#161508 hack: treat oscillation likewise + if ((bSplitError && bTryToSplit) // no restart if we did not try to split: i72847, i79426 + || posSizeOscillationControl.OscillationDetected(*this)) { lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX); setFrameAreaPositionValid(false);