sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx | 44 +++++++++- sw/qa/writerfilter/dmapper/data/field-char-height-header-toc.docx |binary sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx | 6 - sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx | 4 4 files changed, 46 insertions(+), 8 deletions(-)
New commits: commit 8acfabf1f8bbb444d4fc3275a897da0aa0830e5e Author: Miklos Vajna <[email protected]> AuthorDate: Tue Oct 14 13:10:35 2025 +0200 Commit: Xisco Fauli <[email protected]> CommitDate: Mon Oct 20 15:50:33 2025 +0200 tdf#168688 DOCX import: fix missing char props in field in header in ToC Regression from commit 6ee33d0772f991f04e97b5b61d7b11810a1b8ac0 (tdf#104079 RTF import: fix handling fields inside TOC fields, 2017-10-17), open the bugdoc, check the font size of "TEST" in the header, it should be 24pt, but it's 12pt. What happens is that the document ends with the table of contents DOCX field, so the section properties at the end of the document (including a reference to a header) get parsed while the ToC field is still open, so we think that m_xTOCMarkerCursor is in use, but it's for the body text and we're in the header, so it's not relevant. Fix the problem by moving m_xTOCMarkerCursor to m_StreamStateStack, where all state that should be stashed away & restored later belongs. This way the old tdf#104079 use-case keeps working and the new use-case gets fixed. Interestingly this xTOCMarkerCursor is not actually used as a cursor anymore and it just indicates if we're inside a ToC or not, but leave that unchanged in this commit. Change-Id: I1d1c9f55413d94dcde9e7c715bc9f9863f354dcb Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192392 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins (cherry picked from commit 86d0a2f84d714da67d83fdba652540ebe1027382) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192720 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx b/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx index f9861b0dbe2a..a98f682ab6c4 100644 --- a/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx +++ b/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx @@ -7,7 +7,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include <test/unoapi_test.hxx> +#include <swmodeltestbase.hxx> #include <com/sun/star/text/XTextDocument.hpp> #include <com/sun/star/beans/XPropertySet.hpp> @@ -23,17 +23,23 @@ #include <com/sun/star/text/XPageCursor.hpp> #include <vcl/scheduler.hxx> +#include <editeng/fhgtitem.hxx> + +#include <docsh.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <txatbase.hxx> using namespace ::com::sun::star; namespace { /// Tests for sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx. -class Test : public UnoApiTest +class Test : public SwModelTestBase { public: Test() - : UnoApiTest(u"/sw/qa/writerfilter/dmapper/data/"_ustr) + : SwModelTestBase(u"/sw/qa/writerfilter/dmapper/data/"_ustr) { } }; @@ -494,6 +500,38 @@ CPPUNIT_TEST_FIXTURE(Test, testIfField) // load. loadFromFile(u"if-field.docx"); } + +CPPUNIT_TEST_FIXTURE(Test, testFieldCharHeightHeaderToC) +{ + // Given a document with a header that has a field with a custom font size, while the document + // ends with a table of contents: + // When importing that document: + createSwDoc("field-char-height-header-toc.docx"); + + // Then make sure the custom font size is not lost: + SwDocShell* pDocShell = getSwDocShell(); + // Navigate to the start of the only table's B1 cell. + SwDoc* pDoc = pDocShell->GetDoc(); + sw::TableFrameFormats& rTableFormats = *pDoc->GetTableFrameFormats(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rTableFormats.size()); + SwTableFormat* pTableFormat = rTableFormats[0]; + SwTable* pTable = SwTable::FindTable(pTableFormat); + const SwTableBox* pCell = pTable->GetTableBox(u"B1"_ustr); + // Get the font size there. + const SwStartNode* pStartNode = pCell->GetSttNd(); + SwNodeIndex aNodeIndex(*pStartNode); + ++aNodeIndex; + const SwTextNode* pTextNode = aNodeIndex.GetNode().GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrAt(0, RES_TXTATR_AUTOFMT); + // Without the accompanying fix in place, this test would have failed, there was no direct + // formatting, so we got 12pt from style instead of the wanted 24pt. + CPPUNIT_ASSERT(pAttr); + const SwFormatAutoFormat& rAutoFormat + = static_txtattr_cast<SwTextAttrEnd*>(pAttr)->GetAutoFormat(); + const SvxFontHeightItem& rFontHeightItem + = rAutoFormat.GetStyleHandle()->Get(RES_CHRATR_FONTSIZE); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(480), rFontHeightItem.GetHeight()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/writerfilter/dmapper/data/field-char-height-header-toc.docx b/sw/qa/writerfilter/dmapper/data/field-char-height-header-toc.docx new file mode 100644 index 000000000000..eb855c8ac978 Binary files /dev/null and b/sw/qa/writerfilter/dmapper/data/field-char-height-header-toc.docx differ diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx index 5a1e6cbe752b..3bca1637c980 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx +++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx @@ -2707,7 +2707,7 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con { uno::Reference<text::XTextCursor> xCursor; if (m_StreamStateStack.top().bParaHadField - && !IsInComments() && !m_xTOCMarkerCursor.is()) + && !IsInComments() && !m_StreamStateStack.top().xTOCMarkerCursor.is()) { // Workaround to make sure char props of the field are not lost. // Not relevant for editeng-based comments. @@ -7518,7 +7518,7 @@ void DomainMapper_Impl::handleToc const auto xTextCursor = xTextAppend->getText()->createTextCursor(); if (xTextCursor) xTextCursor->gotoEnd(false); - m_xTOCMarkerCursor = xTextCursor; + m_StreamStateStack.top().xTOCMarkerCursor = xTextCursor; } else { @@ -7528,7 +7528,7 @@ void DomainMapper_Impl::handleToc // init [xTOCMarkerCursor] uno::Reference< text::XText > xText = xTextAppend->getText(); - m_xTOCMarkerCursor = xText->createTextCursor(); + m_StreamStateStack.top().xTOCMarkerCursor = xText->createTextCursor(); // create header of the TOC with the TOC title inside createSectionForRange(m_StreamStateStack.top().xSdtEntryStart, xTextRangeEndOfTocHeader, u"com.sun.star.text.IndexHeaderSection", true); diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx index e1a9931ef0fd..237b4a4d1db1 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx +++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx @@ -274,6 +274,8 @@ struct SubstreamContext // inline paragraph: hidden paragraph mark by w:specVanish bool bIsInlineParagraph = false; bool bIsPreviousInlineParagraph = false; + + css::uno::Reference< css::text::XTextCursor > xTOCMarkerCursor; }; /// Information about a paragraph to be finished after a field end. @@ -675,8 +677,6 @@ private: bool m_bIsLastSectionGroup; bool m_bUsingEnhancedFields; - css::uno::Reference< css::text::XTextCursor > m_xTOCMarkerCursor; - ::std::set<::std::pair<PagePartType, PageType>> m_HeaderFooterSeen; // LN_CT_PPrBase_spacing has specified both a line and a lineRule?
