diff --git a/sw/qa/extras/layout/data/toc-inline-heading-order.fodt b/sw/qa/extras/layout/data/toc-inline-heading-order.fodt new file mode 100644 index 000000000000..d5fa5a9dbacf --- /dev/null +++ b/sw/qa/extras/layout/data/toc-inline-heading-order.fodt @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Foo + Bar boo + + + Table of Contents + + + + + + + + + + + + + + Table of Contents + + + + + + diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx index adb0e855ee43..a5c2249a0723 100644 --- a/sw/qa/extras/layout/layout6.cxx +++ b/sw/qa/extras/layout/layout6.cxx @@ -2236,6 +2236,22 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf170846_2) assertXPath(pXmlDoc, "//page[2]//tab", 3); // Three tables on page 2 } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTocInlineHeadingOrder) +{ + createSwDoc("toc-inline-heading-order.fodt"); + dispatchCommand(mxComponent, u".uno:UpdateAllIndexes"_ustr, {}); + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // The TOC is the second section in the body. The body section's three + // txt children carry the entries in document order. + assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[1]/SwParaPortion/SwLineLayout", + "portion", u"Foo"); + assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[2]/SwParaPortion/SwLineLayout", + "portion", u"Bar"); + assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt[3]/SwParaPortion/SwLineLayout", + "portion", u" boo"); +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testInlineTableShiftDown) { createSwDoc("floattable-center-shift-down.docx"); diff --git a/sw/source/core/inc/txmsrt.hxx b/sw/source/core/inc/txmsrt.hxx index 324f88a90f23..bd0734d01aa9 100644 --- a/sw/source/core/inc/txmsrt.hxx +++ b/sw/source/core/inc/txmsrt.hxx @@ -162,6 +162,7 @@ struct SwTOXSortTabBase private: bool m_bValidText; + bool m_bAnchoredAsChar; // used by SwTOXSortTabBase::sort_lt to break ties TextAndReading m_aSort; virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const = 0; diff --git a/sw/source/core/tox/txmsrt.cxx b/sw/source/core/tox/txmsrt.cxx index 58e8a5230d57..bd4b41449cc0 100644 --- a/sw/source/core/tox/txmsrt.cxx +++ b/sw/source/core/tox/txmsrt.cxx @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -139,6 +140,7 @@ SwTOXSortTabBase::SwTOXSortTabBase( TOXSortType nTyp, const SwContentNode* pNd, : pTOXNd( nullptr ), pTextMark( pMark ), pTOXIntl( pInter ), nPos( 0 ), nCntPos( 0 ), nType( o3tl::narrowing(nTyp) ) , m_bValidText( false ) + , m_bAnchoredAsChar( false ) { if ( pLocale ) aLocale = *pLocale; @@ -177,6 +179,10 @@ SwTOXSortTabBase::SwTOXSortTabBase( TOXSortType nTyp, const SwContentNode* pNd, OSL_ENSURE(bResult, "where is the text node"); nPos = aPos.GetNodeIndex(); nCntPos = aPos.GetContentIndex(); + + const SwFrameFormat* pFlyFormat = pNd->GetFlyFormat(); + if (pFlyFormat && pFlyFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) + m_bAnchoredAsChar = true; } } else @@ -291,8 +297,16 @@ bool SwTOXSortTabBase::sort_lt(const SwTOXSortTabBase& rCmp) } else if( pFirst && pFirst->IsTextNode() && pNext && pNext->IsTextNode() ) - return ::IsFrameBehind( *static_cast(pNext), nCntPos, - *static_cast(pFirst), nCntPos ); + { + // An inline (as-char) anchored fly's content is rendered + // at its anchor character position. If the character offset is same + // then the inline fly is rendered before the paragraph. + if (m_bAnchoredAsChar != rCmp.m_bAnchoredAsChar) + return m_bAnchoredAsChar; + + return ::IsFrameBehind(*static_cast(pNext), nCntPos, + *static_cast(pFirst), nCntPos); + } } } return false; diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx index 544447d7e0a9..0aa15988c7fa 100644 --- a/sw/source/uibase/utlui/content.cxx +++ b/sw/source/uibase/utlui/content.cxx @@ -580,10 +580,25 @@ void SwContentType::FillMemberList(bool* pbContentChanged) if (nLevel >= m_nOutlineLevel) continue; double nYPos = m_bAlphabeticSort ? 0 : static_cast(getYPos(*pNode)); - if (nEndOfExtrasIndex >= pNode->GetIndex() && pNode->GetFlyFormat()) + const SwFrameFormat* pFlyFormat = pNode->GetFlyFormat(); + if (nEndOfExtrasIndex >= pNode->GetIndex() && pFlyFormat) { - nYPos += nOutlinesInFramesIndexAdjustment; - nOutlinesInFramesIndexAdjustment += 0.00001; + // For fly nodes getYPos() returns their anchor node's nYPos + const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor(); + const SwPosition* pAnchorPos = rAnchor.GetContentAnchor(); + if (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR && pAnchorPos + && pAnchorPos->GetContentIndex() == 0) + { + // An inline (as-char) fly anchored at the start of its + // anchor paragraph renders before the paragraph's own + // text. Place its outline entry before the anchor's nYPos. + nYPos -= 0.0001; + } + else + { + nYPos += nOutlinesInFramesIndexAdjustment; + nOutlinesInFramesIndexAdjustment += 0.00001; + } } auto pCnt(std::make_unique(this, aEntry, i, nLevel,