sw/qa/extras/layout/data/cell-vertical-align-middle-in-fly-at-page.fodt | 35 ++++++++++ sw/qa/extras/layout/layout5.cxx | 18 +++++ sw/source/core/inc/pagefrm.hxx | 8 ++ sw/source/core/layout/layact.cxx | 10 ++ 4 files changed, 69 insertions(+), 2 deletions(-)
New commits: commit 15c89cf1b72a7556af238d9b0a7d34017c6ec3ee Author: Mike Kaganski <[email protected]> AuthorDate: Sun Nov 2 13:28:36 2025 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sun Nov 2 11:32:11 2025 +0100 tdf#169158: Call FormatObjsAtFrame again, when at-page fly needs that Formatting a cell with non-default vertical alignment is done in two steps. In the end of the first step, SwContentNotify::ImplDestroy detects the difference of size/position of the table content, and the non-default vertical alignment, and invalidates the cell and its page. Then the next formatting would correct the size/position mismatch; it obviously relies on the second pass of formatting. In SwLayAction::InternalAction, the formatting loop did a single call to SwObjectFormatter::FormatObjsAtFrame, which formatted at-page fly objects, and then an inner loop which formatted the rest of the page content. In that inner loop, it performed validation of all flags, then called FormatContent, and checked the validity. That meant, that any invalidation that could happen in at-page objects formatting time would be lost. Then, there was nothing to indicate that the at-page objects could need a second formatting attempt; the outer loop could only run once, even when the at-page formatting needed another run. This change introduces a dedicated flag for at-page fly invalid state. It is checked in SwLayAction::InternalAction in a new loop, to make sure that SwObjectFormatter::FormatObjsAtFrame is repeated as needed. Change-Id: Id6f4fef10cc031af95c38f0074178405027c513d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193298 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/sw/qa/extras/layout/data/cell-vertical-align-middle-in-fly-at-page.fodt b/sw/qa/extras/layout/data/cell-vertical-align-middle-in-fly-at-page.fodt new file mode 100644 index 000000000000..e4be99c21a8b --- /dev/null +++ b/sw/qa/extras/layout/data/cell-vertical-align-middle-in-fly-at-page.fodt @@ -0,0 +1,35 @@ +<?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: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:styles> + <style:style style:name="Frame" style:family="graphic"/> + </office:styles> + <office:automatic-styles> + <style:style style:name="Table1.1" style:family="table-row"> + <style:table-row-properties style:min-row-height="3cm"/> + </style:style> + <style:style style:name="Table1.A1" style:family="table-cell"> + <style:table-cell-properties style:vertical-align="middle" fo:padding="1mm" fo:border="0.06pt solid #000000"/> + </style:style> + <style:style style:name="fr1" style:family="graphic" style:parent-style-name="Frame"> + <style:graphic-properties style:vertical-pos="from-top" style:vertical-rel="page" style:horizontal-pos="from-left" style:horizontal-rel="page" fo:padding="0" fo:border="none"/> + </style:style> + </office:automatic-styles> + <office:body> + <office:text> + <draw:frame draw:style-name="fr1" draw:name="Frame1" text:anchor-type="page" text:anchor-page-number="1" svg:x="3cm" svg:y="3cm" svg:width="5cm" svg:height="3cm"> + <draw:text-box> + <table:table table:name="Table1"> + <table:table-column/> + <table:table-row table:style-name="Table1.1"> + <table:table-cell table:style-name="Table1.A1" office:value-type="string"> + <text:p>foo</text:p> + </table:table-cell> + </table:table-row> + </table:table> + </draw:text-box> + </draw:frame> + <text:p/> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/layout/layout5.cxx b/sw/qa/extras/layout/layout5.cxx index 8abae48b6c81..c9868574d12d 100644 --- a/sw/qa/extras/layout/layout5.cxx +++ b/sw/qa/extras/layout/layout5.cxx @@ -2244,6 +2244,24 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf164718) CPPUNIT_ASSERT_LESS(sal_Int32(4000), height); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf169158) +{ + // A table with vertical-align="middle" in a fly anchored at page: + createSwDoc("cell-vertical-align-middle-in-fly-at-page.fodt"); + auto pXmlDoc = parseLayoutDump(); // Intentionally no Scheduler::ProcessEventsToIdle + + // The problem was, that layout didn't reformat objects at page, when the non-default + // alignment invalidated cells in tables in at-page fly in SwContentNotify::ImplDestroy. + + // Check that the text in the cell is aligned correctly: + assertXPath(pXmlDoc, "//page", 1); + assertXPath(pXmlDoc, "//page/anchored/fly/tab/row/cell/infos/bounds", "top", u"1985"); + // Without the fix, this would fail with + // - Expected: 2698 + // - Actual : 2043 + assertXPath(pXmlDoc, "//page/anchored/fly/tab/row/cell/txt/infos/bounds", "top", u"2698"); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/inc/pagefrm.hxx b/sw/source/core/inc/pagefrm.hxx index 3f4967a65e62..aa2af7e0ae4c 100644 --- a/sw/source/core/inc/pagefrm.hxx +++ b/sw/source/core/inc/pagefrm.hxx @@ -81,6 +81,8 @@ class SW_DLLPUBLIC SwPageFrame final: public SwFootnoteBossFrame bool m_bInvalidAutoCmplWrds :1; // Update auto complete word list bool m_bInvalidWordCount :1; bool m_bHasGrid :1; // Grid for Asian layout + mutable bool m_bInAtPageFlyFormatting : 1 = false; + mutable bool m_bInvalidAtPageFly : 1 = false; // Disambiguate at-page invalidation static const sal_Int8 snShadowPxWidth; @@ -229,6 +231,7 @@ public: inline void ValidateSmartTags() const; inline void ValidateAutoCompleteWords() const; inline void ValidateWordCount() const; + void ValidateAtPageFly() const { m_bInvalidAtPageFly = false; } inline bool IsInvalid() const; inline bool IsInvalidFly() const; bool IsRightShadowNeeded() const; @@ -242,6 +245,9 @@ public: bool IsInvalidSmartTags() const { return m_bInvalidSmartTags; } bool IsInvalidAutoCompleteWords() const { return m_bInvalidAutoCmplWrds; } bool IsInvalidWordCount() const { return m_bInvalidWordCount; } + bool IsInvalidAtPageFly() const { return m_bInvalidAtPageFly; } + bool IsInAtPageFlyFormatting() const { return m_bInAtPageFlyFormatting; } + void SetInAtPageFlyFormatting(bool val) const { m_bInAtPageFlyFormatting = val; } /** SwPageFrame::GetDrawBackgroundColor @@ -373,6 +379,8 @@ inline const SwContentFrame *SwPageFrame::FindLastBodyContent() const inline void SwPageFrame::InvalidateFlyLayout() const { const_cast<SwPageFrame*>(this)->m_bInvalidFlyLayout = true; + if (m_bInAtPageFlyFormatting) + m_bInvalidAtPageFly = true; } inline void SwPageFrame::InvalidateFlyContent() const { diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx index de8e0055e592..923d22b52dd4 100644 --- a/sw/source/core/layout/layact.cxx +++ b/sw/source/core/layout/layact.cxx @@ -570,12 +570,18 @@ void SwLayAction::InternalAction(OutputDevice* pRenderContext) if (!bTakeShortcut) { + bool bAtPageObjectsAreInvalid = false; while ( !IsInterrupt() && !IsNextCycle() && - ((pPage->GetSortedObjs() && pPage->IsInvalidFly()) || pPage->IsInvalid()) ) + ((pPage->GetSortedObjs() && pPage->IsInvalidFly()) || pPage->IsInvalid() || bAtPageObjectsAreInvalid)) { unlockPositionOfObjects( pPage ); - SwObjectFormatter::FormatObjsAtFrame( *pPage, *pPage, this ); + pPage->ValidateAtPageFly(); + pPage->SetInAtPageFlyFormatting(true); + SwObjectFormatter::FormatObjsAtFrame(*pPage, *pPage, this); + pPage->SetInAtPageFlyFormatting(false); + bAtPageObjectsAreInvalid = pPage->IsInvalidAtPageFly(); + if ( !pPage->GetSortedObjs() ) { // If there are no (more) Flys, the flags are superfluous.
