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="&apos;Liberation Sans&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"/>
+   <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);

Reply via email to