sw/qa/extras/layout/data/many-paragraphs-per-page-in-6-columns.odt |binary
 sw/qa/extras/layout/layout5.cxx                                    |   34 +++
 sw/qa/extras/ooxmlexport/ooxmlexport12.cxx                         |    2 
 sw/source/core/layout/laycache.cxx                                 |  101 
+---------
 sw/source/core/layout/layhelp.hxx                                  |    2 
 sw/source/core/layout/tabfrm.cxx                                   |    4 
 6 files changed, 56 insertions(+), 87 deletions(-)

New commits:
commit b6bcc7ac278e0db22df02707789730ec123bf934
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Fri Jun 6 02:05:21 2025 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Sat Jun 7 11:47:47 2025 +0200

    tdf#166871: drop mnMaxParaPerPage hack
    
    It was there since commit 84a3db80b4fd66c6854b3135b5f69b61fd828e62
    (initial import, 2000-09-18); and its idea was to use some heuristics
    to predict the likely number of pages (when there was no metadata, to
    use that number for progress indicator), and, most importantly, to
    predict the page breaks (where a paragraph must be moved to the next
    page, just because there are many paragraphs on this page already; it
    likely was considered an optimization).
    
    But that hack, in fact, creates page breaks in random places, and
    then requires moving stuff back; in some cases, the resulting layout
    was instable, and layout loop control triggered. And that broke the
    most straightforward way of arranging pages, from start to end.
    
    This change removes that hack. The speedup that the hack could
    potentially provide would be negligible, if at all; and the damage
    is real.
    
    I don't know if the following part of documentation comment for the
    constructor of SwLayHelper is still relevant after this change:
    
     "If there's no layout cache, the distribution to the pages is more
      a guess, but a guess with statistical background."
    
    testObjectCrossReference needed a fields update, for unclear reason.
    
    SwTabFrame::Split needed to be improvedto fix failing TestTdf150606,
    where a zero-height column body tried to layout its table; in this
    case, in SwTabFrame::Split pRow was pointing to Lower(), nRowCount
    was 0, nRepeat was 0, and bSplitRowAllowed was false -> it chose
    "bKeepNextRow" path, moved pRow to its next, split it, and a wrong
    single-cell table appeared on page 3. This seems to pre-exist, and
    only happened to be manifested in a test by the change.
    
    Change-Id: I2d203055e67b0155bcd719efb1f8a8b6a82b1156
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186243
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/layout/data/many-paragraphs-per-page-in-6-columns.odt 
b/sw/qa/extras/layout/data/many-paragraphs-per-page-in-6-columns.odt
new file mode 100644
index 000000000000..83f8943e1db9
Binary files /dev/null and 
b/sw/qa/extras/layout/data/many-paragraphs-per-page-in-6-columns.odt differ
diff --git a/sw/qa/extras/layout/layout5.cxx b/sw/qa/extras/layout/layout5.cxx
index e0ca92bd4524..075c1a58417d 100644
--- a/sw/qa/extras/layout/layout5.cxx
+++ b/sw/qa/extras/layout/layout5.cxx
@@ -1751,6 +1751,40 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf166691)
     assertXPath(pXmlDoc, "//page", 1);
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf166871)
+{
+    // Given a document with 6-column-layout pages, having hundreds of small 
paragraphs per page
+    // (normal and headings); its metadata has the number of pages, 
paragraphs, etc.:
+    createSwDoc("many-paragraphs-per-page-in-6-columns.odt");
+    auto pXmlDoc = parseLayoutDump();
+
+    // Check the layout - i.e., the count of paragraphs per column per page.
+    // The metadata in the document must not prevent the correct layout.
+
+    assertXPath(pXmlDoc, "//page", 12);
+
+    // The first 11 pages have the same paragraph count in each column
+    for (int page = 1; page <= 11; ++page)
+    {
+        OString Xpath1 = "//page[" + OString::number(page) + "]/body/column";
+        assertXPath(pXmlDoc, Xpath1, 6);
+        for (int column = 1; column <= 6; ++column)
+        {
+            OString Xpath2 = Xpath1 + "[" + OString::number(column) + 
"]/body/txt";
+            assertXPath(pXmlDoc, Xpath2, 47);
+        }
+    }
+
+    // Check the last page
+    assertXPath(pXmlDoc, "//page[12]/body/column", 6);
+    assertXPath(pXmlDoc, "//page[12]/body/column[1]/body/txt", 14);
+    assertXPath(pXmlDoc, "//page[12]/body/column[2]/body/txt", 0);
+    assertXPath(pXmlDoc, "//page[12]/body/column[3]/body/txt", 0);
+    assertXPath(pXmlDoc, "//page[12]/body/column[4]/body/txt", 0);
+    assertXPath(pXmlDoc, "//page[12]/body/column[5]/body/txt", 0);
+    assertXPath(pXmlDoc, "//page[12]/body/column[6]/body/txt", 0);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
index 19bca2e1b238..fedcbf31e53a 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
@@ -18,6 +18,7 @@
 #include <com/sun/star/text/XTextTablesSupplier.hpp>
 #include <com/sun/star/text/XTextTable.hpp>
 #include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
 #include <o3tl/string_view.hxx>
 
 class Test : public SwModelTestBase
@@ -468,6 +469,7 @@ CPPUNIT_TEST_FIXTURE(Test, testObjectCrossReference)
     uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, 
uno::UNO_QUERY);
     uno::Reference<container::XEnumerationAccess> xFieldsAccess(
         xTextFieldsSupplier->getTextFields());
+    xFieldsAccess.queryThrow<util::XRefreshable>()->refresh();
     uno::Reference<container::XEnumeration> 
xFields(xFieldsAccess->createEnumeration());
     CPPUNIT_ASSERT(xFields->hasMoreElements());
 
diff --git a/sw/source/core/layout/laycache.cxx 
b/sw/source/core/layout/laycache.cxx
index a3f94a56daf1..4c1df8a6b286 100644
--- a/sw/source/core/layout/laycache.cxx
+++ b/sw/source/core/layout/laycache.cxx
@@ -528,8 +528,6 @@ SwLayHelper::SwLayHelper( SwDoc& rDoc, SwFrame* &rpF, 
SwFrame* &rpP, SwPageFrame
     , mrpActualSection( rpA )
     , mbBreakAfter(false)
     , mrDoc(rDoc)
-    , mnMaxParaPerPage( 25 )
-    , mnParagraphCnt( bCache ? 0 : USHRT_MAX )
     , mnFlyIdx( 0 )
     , mbFirst( bCache )
 {
@@ -541,7 +539,6 @@ SwLayHelper::SwLayHelper( SwDoc& rDoc, SwFrame* &rpF, 
SwFrame* &rpP, SwPageFrame
         {
             mnIndex = 0;
             mnStartOfContent = 
rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex();
-            mnMaxParaPerPage = 1000;
         }
         else
         {
@@ -600,28 +597,8 @@ sal_uLong SwLayHelper::CalcPageCount()
             if ( nTmp > SwNodeOffset(0) )
                 nNdCount = sal_Int32(nTmp);
         }
-        if ( nNdCount > 100 ) // no estimation below this value
-        {
-            if ( nPgCount > 0 )
-            {   // tdf#129529 avoid 0...
-                mnMaxParaPerPage = std::max<sal_uLong>(3, nNdCount / nPgCount);
-            }
-            else
-            {
-                mnMaxParaPerPage = std::max( sal_uLong(20),
-                                       sal_uLong(20 + nNdCount / 1000 * 3) );
-                const sal_uLong nMax = 53;
-                mnMaxParaPerPage = std::min( mnMaxParaPerPage, nMax );
-                nPgCount = nNdCount / mnMaxParaPerPage;
-            }
-            if ( nNdCount < 1000 )
-                nPgCount = 0;// no progress bar for small documents
-            SwViewShell *pSh = nullptr;
-            if( mrpLay && mrpLay->getRootFrame() )
-                pSh = mrpLay->getRootFrame()->GetCurrShell();
-            if( pSh && pSh->GetViewOptions()->getBrowseMode() )
-                mnMaxParaPerPage *= 6;
-        }
+        if ( nNdCount < 1000 )
+            nPgCount = 0;// no progress bar for small documents
     }
     return nPgCount;
 }
@@ -721,13 +698,10 @@ bool SwLayHelper::CheckInsertPage(
 bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
 {
     bool bRet = false;
-    bool bLongTab = false;
-    sal_uLong nMaxRowPerPage( 0 );
     nNodeIndex -= mnStartOfContent;
     sal_uInt16 nRows( 0 );
     if( mrpFrame->IsTabFrame() )
     {
-        //Inside a table counts every row as a paragraph
         SwFrame *pLow = static_cast<SwTabFrame*>(mrpFrame)->Lower();
         nRows = 0;
         do
@@ -735,89 +709,47 @@ bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
             ++nRows;
             pLow = pLow->GetNext();
         } while ( pLow );
-        mnParagraphCnt += nRows;
-        if( !mpImpl && mnParagraphCnt > mnMaxParaPerPage + 10 )
-        {
-            // OD 09.04.2003 #108698# - improve heuristics:
-            // Assume that a table, which has more than three times the 
quantity
-            // of maximal paragraphs per page rows, consists of rows, which 
have
-            // the height of a normal paragraph. Thus, allow as much rows per 
page
-            // as much paragraphs are allowed.
-            if ( nRows > ( 3*mnMaxParaPerPage ) )
-            {
-                nMaxRowPerPage = mnMaxParaPerPage;
-            }
-            else
-            {
-                SwFrame *pTmp = static_cast<SwTabFrame*>(mrpFrame)->Lower();
-                if( pTmp->GetNext() )
-                    pTmp = pTmp->GetNext();
-                pTmp = static_cast<SwRowFrame*>(pTmp)->Lower();
-                sal_uInt16 nCnt = 0;
-                do
-                {
-                    ++nCnt;
-                    pTmp = pTmp->GetNext();
-                } while( pTmp );
-                nMaxRowPerPage = std::max( sal_uLong(2), mnMaxParaPerPage / 
nCnt );
-            }
-            bLongTab = true;
-        }
     }
-    else
-        ++mnParagraphCnt;
     if( mbFirst && mpImpl && mnIndex < mpImpl->size() &&
         mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex &&
         ( mpImpl->GetBreakOfst( mnIndex ) < COMPLETE_STRING ||
           ( ++mnIndex < mpImpl->size() &&
           mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) ) )
         mbFirst = false;
-    // OD 09.04.2003 #108698# - always split a big tables.
-    if ( !mbFirst ||
-         ( mrpFrame->IsTabFrame() && bLongTab )
-       )
+    if (!mbFirst)
     {
         sal_Int32 nRowCount = 0;
         do
         {
-            if( mpImpl || bLongTab )
+            if (mpImpl)
             {
                 sal_Int32 nOfst = COMPLETE_STRING;
                 sal_uInt16 nType = SW_LAYCACHE_IO_REC_PAGES;
-                if( bLongTab )
+                while( mnIndex < mpImpl->size() &&
+                       mpImpl->GetBreakIndex(mnIndex) < nNodeIndex)
+                    ++mnIndex;
+                if( mnIndex < mpImpl->size() &&
+                    mpImpl->GetBreakIndex(mnIndex) == nNodeIndex )
                 {
+                    nType = mpImpl->GetBreakType( mnIndex );
+                    nOfst = mpImpl->GetBreakOfst( mnIndex++ );
                     mbBreakAfter = true;
-                    nOfst = static_cast<sal_Int32>(nRowCount + nMaxRowPerPage);
-                }
-                else
-                {
-                    while( mnIndex < mpImpl->size() &&
-                           mpImpl->GetBreakIndex(mnIndex) < nNodeIndex)
-                        ++mnIndex;
-                    if( mnIndex < mpImpl->size() &&
-                        mpImpl->GetBreakIndex(mnIndex) == nNodeIndex )
-                    {
-                        nType = mpImpl->GetBreakType( mnIndex );
-                        nOfst = mpImpl->GetBreakOfst( mnIndex++ );
-                        mbBreakAfter = true;
-                    }
                 }
 
                 if( nOfst < COMPLETE_STRING )
                 {
                     bool bSplit = false;
                     sal_uInt16 nRepeat( 0 );
-                    if( !bLongTab && mrpFrame->IsTextFrame() &&
+                    if( mrpFrame->IsTextFrame() &&
                         SW_LAYCACHE_IO_REC_PARA == nType &&
                         nOfst < 
static_cast<SwTextFrame*>(mrpFrame)->GetText().getLength())
                         bSplit = true;
                     else if( mrpFrame->IsTabFrame() && nRowCount < nOfst &&
-                             ( bLongTab || SW_LAYCACHE_IO_REC_TABLE == nType ) 
)
+                             ( SW_LAYCACHE_IO_REC_TABLE == nType ) )
                     {
                         nRepeat = static_cast<SwTabFrame*>(mrpFrame)->
                                   GetTable()->GetRowsToRepeat();
                         bSplit = nOfst < nRows && nRowCount + nRepeat < nOfst;
-                        bLongTab = bLongTab && bSplit;
                     }
                     if( bSplit )
                     {
@@ -891,7 +823,7 @@ bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
             }
 
             SwPageFrame* pLastPage = mrpPage;
-            if (CheckInsertPage(mrpPage, mrpLay, mrpFrame, mbBreakAfter, 
mnMaxParaPerPage < mnParagraphCnt))
+            if (CheckInsertPage(mrpPage, mrpLay, mrpFrame, mbBreakAfter, 
false))
             {
                 CheckFlyCache_( pLastPage );
                 if( mrpPrv && mrpPrv->IsTextFrame() && 
!mrpPrv->isFrameAreaSizeValid() )
@@ -902,7 +834,6 @@ bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
 
                 bRet = true;
                 mrpPrv = nullptr;
-                mnParagraphCnt = 0;
 
                 if ( mrpActualSection )
                 {
@@ -942,8 +873,8 @@ bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
                         mrpLay = mrpLay->GetNextLayoutLeaf();
                 }
             }
-        } while( bLongTab || ( mpImpl && mnIndex < mpImpl->size() &&
-                 mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) );
+        } while( mpImpl && mnIndex < mpImpl->size() &&
+                 mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex );
     }
     mbFirst = false;
     return bRet;
diff --git a/sw/source/core/layout/layhelp.hxx 
b/sw/source/core/layout/layhelp.hxx
index 356caa4c14d1..91f8e259b93e 100644
--- a/sw/source/core/layout/layhelp.hxx
+++ b/sw/source/core/layout/layhelp.hxx
@@ -114,8 +114,6 @@ class SwLayHelper
     bool mbBreakAfter;
     SwDoc& mrDoc;
     SwLayCacheImpl* mpImpl;
-    sal_uLong mnMaxParaPerPage;
-    sal_uLong mnParagraphCnt;
     SwNodeOffset mnStartOfContent;
     size_t mnIndex;                          ///< the index in the page break 
array
     size_t mnFlyIdx;                         ///< the index in the fly cache 
array
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index f98444f7d410..c3fffa454f47 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -1124,6 +1124,10 @@ bool SwTabFrame::Split(const SwTwips nCutPos, bool 
bTryToSplit,
     // and compare with the available height
     SwTwips nRemainingSpaceForLastRow = aRectFnSet.YDiff(nCutPos, 
aRectFnSet.GetPrtTop(*this));
     nRemainingSpaceForLastRow -= aRectFnSet.GetBottomMargin(*this);
+
+    if (nRemainingSpaceForLastRow <= 0)
+        return false; // upper has no space for this table at all
+
     auto getRemainingAfter = [aRectFnSet, nAvailable = 
nRemainingSpaceForLastRow,
                               nFirstRowTop = 
aRectFnSet.GetTop(pRow->getFrameArea())](SwFrame* p)
     { return nAvailable + (p ? aRectFnSet.BottomDist(p->getFrameArea(), 
nFirstRowTop) : 0); };

Reply via email to