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();
