sw/qa/extras/layout/data/tdf167326.fodt |   26 +++
 sw/qa/extras/layout/layout4.cxx         |   27 ++++
 sw/source/core/layout/tabfrm.cxx        |  214 +++++++++++++++++---------------
 3 files changed, 167 insertions(+), 100 deletions(-)

New commits:
commit 80850b7d086e2b0fc7f792130cce951d5d946573
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sat Oct 11 22:30:39 2025 +0500
Commit:     Adolfo Jayme Barrientos <[email protected]>
CommitDate: Mon Oct 20 17:31:17 2025 +0200

    tdf#167326: Let CalcHeightWithFlys take hidden frame state into account
    
    Table cells use that function to calculate the min cell height. In the
    bugdoc, sections inside a cell contained flys, and were hidden. When
    the min cell height was calculated, the sections' flys were considered,
    despite they were hidden.
    
    Change-Id: Ie5a8fb54a1c9fe21cc5f4d474bd24feabb660330
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192232
    Reviewed-by: Mike Kaganski <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit 0ba44065385cbee7c490cf4a97641c19edc79a63)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192713
    Reviewed-by: Adolfo Jayme Barrientos <[email protected]>

diff --git a/sw/qa/extras/layout/data/tdf167326.fodt 
b/sw/qa/extras/layout/data/tdf167326.fodt
new file mode 100644
index 000000000000..5004f9ed9cbf
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf167326.fodt
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office: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:draw="urn:oasis:names:tc:opendocument:xmlns:drawing: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.4" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:automatic-styles>
+  <style:style style:name="gr1" style:family="graphic">
+   <style:graphic-properties style:wrap="none" style:vertical-pos="top" 
style:vertical-rel="paragraph" style:horizontal-pos="center" 
style:horizontal-rel="paragraph" style:flow-with-text="true"/>
+  </style:style>
+ </office:automatic-styles>
+ <office:body>
+  <office:text>
+   <table:table>
+    <table:table-column/>
+    <table:table-row>
+     <table:table-cell office:value-type="string">
+      <text:p>In this cell, below, there is a section with a shape:</text:p>
+      <text:section text:name="Sect1">
+       <text:p><draw:custom-shape text:anchor-type="char" draw:name="Shape1" 
draw:style-name="gr1" svg:width="5cm" svg:height="5cm" svg:x="0" svg:y="0">
+         <draw:enhanced-geometry draw:type="rectangle"/>
+        </draw:custom-shape></text:p>
+      </text:section>
+     </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/layout4.cxx b/sw/qa/extras/layout/layout4.cxx
index a1a9db6db57b..669d2a484100 100644
--- a/sw/qa/extras/layout/layout4.cxx
+++ b/sw/qa/extras/layout/layout4.cxx
@@ -1434,6 +1434,33 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter4, 
testTdf160958_orphans)
     assertXPath(pExportDump, 
"//page[2]/body/txt[1]/SwParaPortion/SwLineLayout", 1);
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter4, testTdf167326)
+{
+    // A document with a table with a section with a to-char no-wrap 
keep-inside-boundaries shape
+    createSwDoc("tdf167326.fodt");
+    auto pExportDump = parseLayoutDump();
+    assertXPath(pExportDump, "//tab", 1);
+    OUString height = getXPath(pExportDump, "//tab/infos/bounds", "height");
+    // Expected height: ~3388 = shape 2836 + 2 * text ~276
+    CPPUNIT_ASSERT_GREATER(sal_Int32(3000), height.toInt32());
+
+    // Hide the section
+    auto xTextSectionsSupplier = 
mxComponent.queryThrow<css::text::XTextSectionsSupplier>();
+    auto xSections = xTextSectionsSupplier->getTextSections();
+    CPPUNIT_ASSERT(xSections);
+    auto xSection = 
xSections->getByName(u"Sect1"_ustr).queryThrow<css::beans::XPropertySet>();
+    xSection->setPropertyValue(u"IsVisible"_ustr, css::uno::Any(false));
+
+    calcLayout();
+    pExportDump = parseLayoutDump();
+    assertXPath(pExportDump, "//tab", 1);
+    height = getXPath(pExportDump, "//tab/infos/bounds", "height");
+    // Expected height: ~276 = 1 * text ~276
+    // Without the fix, this was ~3111, because calculation of table cell's 
height considered
+    // height of section's shape, not taking its visibility into account.
+    CPPUNIT_ASSERT_LESS(sal_Int32(500), height.toInt32());
+}
+
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter4, testTdf161368)
 {
     // Given a document with a text body width of 116 mm, greater than 65535 
twips (115.6 mm)
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index d79568dcf44e..a08ce1af68e2 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -4789,127 +4789,141 @@ void SwRowFrame::dumpAsXml(xmlTextWriterPtr writer) 
const
     (void)xmlTextWriterEndElement(writer);
 }
 
-tools::Long CalcHeightWithFlys( const SwFrame *pFrame )
+static tools::Long CalcHeightWithFlys_Impl(const SwFrame* pTmp, const SwFrame* 
pFrame,
+                                           const SwRectFnSet& aRectFnSet)
 {
-    SwRectFnSet aRectFnSet(pFrame);
+    if (pFrame->IsHiddenNow())
+        return 0;
     tools::Long nHeight = 0;
-    const SwFrame* pTmp = pFrame->IsSctFrame() ?
-            static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : 
pFrame;
-    while( pTmp )
-    {
-        // #i26945# - consider follow text frames
-        const SwSortedObjs* pObjs( nullptr );
-        bool bIsFollow( false );
-        if ( pTmp->IsTextFrame() && static_cast<const 
SwTextFrame*>(pTmp)->IsFollow() )
-        {
-            const SwFrame* pMaster;
-            // #i46450# Master does not necessarily have
-            // to exist if this function is called from JoinFrame() ->
-            // Cut() -> Shrink()
-            const SwTextFrame* pTmpFrame = static_cast<const 
SwTextFrame*>(pTmp);
-            if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
-                 static_cast<const 
SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
-                 static_cast<const 
SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
-                 pMaster = nullptr;
-            else
-                 pMaster = pTmpFrame->FindMaster();
-
-            if ( pMaster )
-            {
-                pObjs = static_cast<const 
SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
-                bIsFollow = true;
-            }
-        }
+    // #i26945# - consider follow text frames
+    const SwSortedObjs* pObjs( nullptr );
+    bool bIsFollow( false );
+    if ( pTmp->IsTextFrame() && static_cast<const 
SwTextFrame*>(pTmp)->IsFollow() )
+    {
+        const SwFrame* pMaster;
+        // #i46450# Master does not necessarily have
+        // to exist if this function is called from JoinFrame() ->
+        // Cut() -> Shrink()
+        const SwTextFrame* pTmpFrame = static_cast<const SwTextFrame*>(pTmp);
+        if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
+             static_cast<const 
SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
+             static_cast<const 
SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
+             pMaster = nullptr;
         else
+             pMaster = pTmpFrame->FindMaster();
+
+        if ( pMaster )
         {
-            pObjs = pTmp->GetDrawObjs();
+            pObjs = static_cast<const 
SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
+            bIsFollow = true;
         }
-        if ( pObjs )
+    }
+    else
+    {
+        pObjs = pTmp->GetDrawObjs();
+    }
+    if ( pObjs )
+    {
+        for (SwAnchoredObject* pAnchoredObj : *pObjs)
         {
-            for (SwAnchoredObject* pAnchoredObj : *pObjs)
+            // #i26945# - if <pTmp> is follow, the
+            // anchor character frame has to be <pTmp>.
+            if ( bIsFollow &&
+                 pAnchoredObj->FindAnchorCharFrame() != pTmp )
             {
-                // #i26945# - if <pTmp> is follow, the
-                // anchor character frame has to be <pTmp>.
-                if ( bIsFollow &&
-                     pAnchoredObj->FindAnchorCharFrame() != pTmp )
+                continue;
+            }
+            // #i26945# - consider also drawing objects
+            {
+                // OD 30.09.2003 #i18732# - only objects, which follow
+                // the text flow have to be considered.
+                const SwFrameFormat* pFrameFormat = 
pAnchoredObj->GetFrameFormat();
+                bool bFollowTextFlow = 
pFrameFormat->GetFollowTextFlow().GetValue();
+                bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != FAR_AWAY;
+                const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
+                bool bIsAnchoredToTmpFrm = false;
+                if ( pPageFrm && pPageFrm->IsPageFrame() && 
pAnchoredObj->GetPageFrame())
+                    bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == 
pPageFrm ||
+                    (pPageFrm->GetFormatPage().GetPhyPageNum() == 
pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
+                const bool bConsiderObj =
+                    (pFrameFormat->GetAnchor().GetAnchorId() != 
RndStdIds::FLY_AS_CHAR) &&
+                    bIsFarAway &&
+                    bFollowTextFlow && bIsAnchoredToTmpFrm;
+                bool bWrapThrough = pFrameFormat->GetSurround().GetValue() == 
text::WrapTextMode_THROUGH;
+                bool bInBackground = !pFrameFormat->GetOpaque().GetValue();
+                // Legacy render requires in-background setting, the new mode 
does not.
+                bool bConsiderFollowTextFlow = bInBackground
+                                               || 
!pFrameFormat->getIDocumentSettingAccess().get(
+                                                   
DocumentSettingId::USE_FORMER_TEXT_WRAPPING);
+                if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && 
bConsiderFollowTextFlow)
                 {
+                    // Ignore wrap-through objects when determining the cell 
height.
+                    // Normally FollowTextFlow requires a resize of the cell, 
but not in case of
+                    // wrap-through.
                     continue;
                 }
-                // #i26945# - consider also drawing objects
+
+                if ( bConsiderObj )
                 {
-                    // OD 30.09.2003 #i18732# - only objects, which follow
-                    // the text flow have to be considered.
-                    const SwFrameFormat* pFrameFormat = 
pAnchoredObj->GetFrameFormat();
-                    bool bFollowTextFlow = 
pFrameFormat->GetFollowTextFlow().GetValue();
-                    bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != 
FAR_AWAY;
-                    const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
-                    bool bIsAnchoredToTmpFrm = false;
-                    if ( pPageFrm && pPageFrm->IsPageFrame() && 
pAnchoredObj->GetPageFrame())
-                        bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == 
pPageFrm ||
-                        (pPageFrm->GetFormatPage().GetPhyPageNum() == 
pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
-                    const bool bConsiderObj =
-                        (pFrameFormat->GetAnchor().GetAnchorId() != 
RndStdIds::FLY_AS_CHAR) &&
-                        bIsFarAway &&
-                        bFollowTextFlow && bIsAnchoredToTmpFrm;
-                    bool bWrapThrough = pFrameFormat->GetSurround().GetValue() 
== text::WrapTextMode_THROUGH;
-                    bool bInBackground = !pFrameFormat->GetOpaque().GetValue();
-                    // Legacy render requires in-background setting, the new 
mode does not.
-                    bool bConsiderFollowTextFlow = bInBackground
-                                                   || 
!pFrameFormat->getIDocumentSettingAccess().get(
-                                                       
DocumentSettingId::USE_FORMER_TEXT_WRAPPING);
-                    if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough 
&& bConsiderFollowTextFlow)
+                    const SwFormatFrameSize &rSz = 
pFrameFormat->GetFrameSize();
+                    if( !rSz.GetHeightPercent() )
                     {
-                        // Ignore wrap-through objects when determining the 
cell height.
-                        // Normally FollowTextFlow requires a resize of the 
cell, but not in case of
-                        // wrap-through.
-                        continue;
-                    }
+                        const SwTwips nDistOfFlyBottomToAnchorTop =
+                            aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
+                                ( aRectFnSet.IsVert() ?
+                                  pAnchoredObj->GetCurrRelPos().X() :
+                                  pAnchoredObj->GetCurrRelPos().Y() );
+
+                        const SwTwips nFrameDiff =
+                            aRectFnSet.YDiff(
+                                aRectFnSet.GetTop(pTmp->getFrameArea()),
+                                aRectFnSet.GetTop(pFrame->getFrameArea()) );
+
+                        nHeight = std::max( nHeight, 
nDistOfFlyBottomToAnchorTop + nFrameDiff -
+                                        
aRectFnSet.GetHeight(pFrame->getFrameArea()) );
 
-                    if ( bConsiderObj )
-                    {
-                        const SwFormatFrameSize &rSz = 
pFrameFormat->GetFrameSize();
-                        if( !rSz.GetHeightPercent() )
-                        {
-                            const SwTwips nDistOfFlyBottomToAnchorTop =
-                                
aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
-                                    ( aRectFnSet.IsVert() ?
-                                      pAnchoredObj->GetCurrRelPos().X() :
-                                      pAnchoredObj->GetCurrRelPos().Y() );
-
-                            const SwTwips nFrameDiff =
-                                aRectFnSet.YDiff(
-                                    aRectFnSet.GetTop(pTmp->getFrameArea()),
-                                    aRectFnSet.GetTop(pFrame->getFrameArea()) 
);
-
-                            nHeight = std::max( nHeight, 
nDistOfFlyBottomToAnchorTop + nFrameDiff -
-                                            
aRectFnSet.GetHeight(pFrame->getFrameArea()) );
-
-                            // #i56115# The first height calculation
-                            // gives wrong results if 
pFrame->getFramePrintArea().Y() > 0. We do
-                            // a second calculation based on the actual 
rectangles of
-                            // pFrame and pAnchoredObj, and use the maximum of 
the results.
-                            // I do not want to remove the first calculation 
because
-                            // if clipping has been applied, using the 
GetCurrRelPos
-                            // might be the better option to calculate nHeight.
-                            const SwTwips nDistOfFlyBottomToAnchorTop2 = 
aRectFnSet.YDiff(
-                                                                            
aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
-                                                                            
aRectFnSet.GetBottom(pFrame->getFrameArea()) );
-
-                            nHeight = std::max( nHeight, 
tools::Long(nDistOfFlyBottomToAnchorTop2 ));
-                        }
+                        // #i56115# The first height calculation
+                        // gives wrong results if 
pFrame->getFramePrintArea().Y() > 0. We do
+                        // a second calculation based on the actual rectangles 
of
+                        // pFrame and pAnchoredObj, and use the maximum of the 
results.
+                        // I do not want to remove the first calculation 
because
+                        // if clipping has been applied, using the 
GetCurrRelPos
+                        // might be the better option to calculate nHeight.
+                        const SwTwips nDistOfFlyBottomToAnchorTop2 = 
aRectFnSet.YDiff(
+                                                                        
aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
+                                                                        
aRectFnSet.GetBottom(pFrame->getFrameArea()) );
+
+                        nHeight = std::max( nHeight, 
tools::Long(nDistOfFlyBottomToAnchorTop2 ));
                     }
                 }
             }
         }
-        if( !pFrame->IsSctFrame() )
-            break;
-        pTmp = pTmp->FindNextCnt();
-        if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
-            break;
     }
     return nHeight;
 }
 
+tools::Long CalcHeightWithFlys( const SwFrame *pFrame )
+{
+    SwRectFnSet aRectFnSet(pFrame);
+    if (pFrame->IsSctFrame())
+    {
+        if (pFrame->IsHiddenNow())
+            return 0;
+        tools::Long nHeight = 0;
+        const SwFrame* pTmp = static_cast<const 
SwSectionFrame*>(pFrame)->ContainsContent();
+        while (pTmp)
+        {
+            nHeight += CalcHeightWithFlys_Impl(pTmp, pFrame, aRectFnSet);
+            pTmp = pTmp->FindNextCnt();
+            if (!static_cast<const SwSectionFrame*>(pFrame)->IsAnLower(pTmp))
+                break;
+        }
+        return nHeight;
+    }
+    // Not a section frame
+    return CalcHeightWithFlys_Impl(pFrame, pFrame, aRectFnSet);
+}
+
 static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame& rCell, const 
SwBorderAttrs& rAttrs )
 {
     const SwTabFrame* pTab = rCell.FindTabFrame();

Reply via email to