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 <mike.kaganski@collabora.com>
Tested-by: Jenkins
This commit is contained in:
Mike Kaganski
2025-10-11 21:54:21 +02:00
parent 5e627c8422
commit 0ba4406538
3 changed files with 179 additions and 112 deletions
+26
View File
@@ -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>
+27
View File
@@ -1443,6 +1443,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)
+126 -112
View File
@@ -4789,125 +4789,139 @@ void SwRowFrame::dumpAsXml(xmlTextWriterPtr writer) const
(void)xmlTextWriterEndElement(writer);
}
static tools::Long CalcHeightWithFlys_Impl(const SwFrame* pTmp, const SwFrame* pFrame,
const SwRectFnSet& aRectFnSet)
{
if (pFrame->IsHiddenNow())
return 0;
tools::Long nHeight = 0;
// #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;
}
}
else
{
pObjs = pTmp->GetDrawObjs();
}
if ( pObjs )
{
for (SwAnchoredObject* pAnchoredObj : *pObjs)
{
// #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;
}
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 ));
}
}
}
}
}
return nHeight;
}
tools::Long CalcHeightWithFlys( const SwFrame *pFrame )
{
SwRectFnSet aRectFnSet(pFrame);
tools::Long nHeight = 0;
const SwFrame* pTmp = pFrame->IsSctFrame() ?
static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : pFrame;
while( pTmp )
if (pFrame->IsSctFrame())
{
// #i26945# - consider follow text frames
const SwSortedObjs* pObjs( nullptr );
bool bIsFollow( false );
if ( pTmp->IsTextFrame() && static_cast<const SwTextFrame*>(pTmp)->IsFollow() )
if (pFrame->IsHiddenNow())
return 0;
tools::Long nHeight = 0;
const SwFrame* pTmp = static_cast<const SwSectionFrame*>(pFrame)->ContainsContent();
while (pTmp)
{
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;
}
nHeight += CalcHeightWithFlys_Impl(pTmp, pFrame, aRectFnSet);
pTmp = pTmp->FindNextCnt();
if (!static_cast<const SwSectionFrame*>(pFrame)->IsAnLower(pTmp))
break;
}
else
{
pObjs = pTmp->GetDrawObjs();
}
if ( pObjs )
{
for (SwAnchoredObject* pAnchoredObj : *pObjs)
{
// #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;
}
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 ));
}
}
}
}
}
if( !pFrame->IsSctFrame() )
break;
pTmp = pTmp->FindNextCnt();
if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
break;
return nHeight;
}
return nHeight;
// Not a section frame
return CalcHeightWithFlys_Impl(pFrame, pFrame, aRectFnSet);
}
static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame& rCell, const SwBorderAttrs& rAttrs )