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 52b423993a5d34927c11674f8bb6e2c9a6ad927f Author: Miklos Vajna <[email protected]> AuthorDate: Tue Oct 14 13:10:35 2025 +0200 Commit: Caolán McNamara <[email protected]> CommitDate: Tue Oct 14 14:06:26 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/+/192367 Tested-by: Caolán McNamara <[email protected]> Reviewed-by: Caolán McNamara <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx b/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx index 040282e79d62..ddefd9e2faa0 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) { } }; @@ -478,6 +484,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 b075246ee417..27b3bcf19b4d 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx +++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx @@ -2670,7 +2670,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. @@ -7372,7 +7372,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 { @@ -7382,7 +7382,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 96501148f981..fee2b89bd2ef 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?
