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 73fd1ac3a8088edebe4b8e755e123ec201eb77e5 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Tue Jun 11 17:39:26 2024 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Nov 29 10:05:05 2024 +0500 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 cda801c4334c..70debda65dd2 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -1899,6 +1899,16 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf155611) // Also must not crash on close because of a frame that accidentally fell off of the layout } +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", 0); + assertXPath(pExportDump, "//page[2]/body/tab", 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 4d712325a3ff..a95b311d5999 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -1922,6 +1922,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 @@ -2116,6 +2139,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() ) { @@ -2806,7 +2830,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);