sw/qa/extras/rtfexport/data/tdf162342.rtf | 7 ++ sw/qa/extras/rtfexport/rtfexport8.cxx | 64 +++++++++++++++++++ sw/source/filter/ww8/attributeoutputbase.hxx | 2 sw/source/filter/ww8/docxattributeoutput.cxx | 3 sw/source/filter/ww8/docxattributeoutput.hxx | 2 sw/source/filter/ww8/rtfattributeoutput.cxx | 9 +- sw/source/filter/ww8/rtfattributeoutput.hxx | 4 - sw/source/filter/ww8/ww8atr.cxx | 3 sw/source/filter/ww8/ww8attributeoutput.hxx | 2 sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx | 34 ++++++++-- 10 files changed, 112 insertions(+), 18 deletions(-)
New commits: commit 1567f48ec9fe096da386bdaf370bc95414c8d923 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Mon Jul 28 01:29:18 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Mon Jul 28 16:35:19 2025 +0200 tdf#162342: workaround out-of-order context stack pop Turns out, the context stack is populated in one order, but emptied in another. Commit e63a75e00dcd797b0bd061add3c72dcb7b353007 (tdf#158586 writerfilter: RTF import: fix \page \sect \skbnone, 2024-02-01) added yet another case, which resulted in m_pTopContext being empty at the moment when section properties should have been applied (see "needed for page properties" comment in DomainMapper::sprmWithProps; that code wasn't reached because of (!rContext) check above). But that isn't the only case; I tried to assert in non-RTF cases, and it fired in `make check`, too. So here is an UGLY HACK. I repair the stack when it's popped out of order. This pleads for a proper fix, but the effort will be far from trivial. Change-Id: I1a5c646188f3da4842d103b8edeb11b801fea830 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188449 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/sw/qa/extras/rtfexport/data/tdf162342.rtf b/sw/qa/extras/rtfexport/data/tdf162342.rtf new file mode 100644 index 000000000000..e04fdc6aeb96 --- /dev/null +++ b/sw/qa/extras/rtfexport/data/tdf162342.rtf @@ -0,0 +1,7 @@ +{ tf1 +\margl720 \margr360 \margt720 \margb720 \paperw6120 \paperh7920 +First +\page +Second +\page +Third} \ No newline at end of file diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx b/sw/qa/extras/rtfexport/rtfexport8.cxx index fbb9acdfb000..c2aa3ef1ec2b 100644 --- a/sw/qa/extras/rtfexport/rtfexport8.cxx +++ b/sw/qa/extras/rtfexport/rtfexport8.cxx @@ -1211,6 +1211,70 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167569_2) } } +CPPUNIT_TEST_FIXTURE(Test, testTdf162342) +{ + // Given an RTF with some page settings, and two page breaks: + createSwDoc("tdf162342.rtf"); + + { + xmlDocUniquePtr pLayout = parseLayoutDump(); + assertXPath(pLayout, "//page['pass 1']", 3); + + // Without the fix, this failed with + // - Expected: 6120 + // - Actual : 12240 + // i.e., the page geometry was ignored. + assertXPath(pLayout, "//page[1]['pass 1']/infos/bounds", "width", u"6120"); + assertXPath(pLayout, "//page[1]['pass 1']/infos/bounds", "height", u"7937"); // Not 7920? + assertXPath(pLayout, "//page[1]['pass 1']/infos/prtBounds", "left", u"720"); + assertXPath(pLayout, "//page[1]['pass 1']/infos/prtBounds", "top", u"720"); + assertXPath(pLayout, "//page[1]['pass 1']/infos/prtBounds", "width", u"5040"); + assertXPath(pLayout, "//page[1]['pass 1']/infos/prtBounds", "height", u"6497"); + + assertXPath(pLayout, "//page[2]['pass 1']/infos/bounds", "width", u"6120"); + assertXPath(pLayout, "//page[2]['pass 1']/infos/bounds", "height", u"7937"); + assertXPath(pLayout, "//page[2]['pass 1']/infos/prtBounds", "left", u"720"); + assertXPath(pLayout, "//page[2]['pass 1']/infos/prtBounds", "top", u"720"); + assertXPath(pLayout, "//page[2]['pass 1']/infos/prtBounds", "width", u"5040"); + assertXPath(pLayout, "//page[2]['pass 1']/infos/prtBounds", "height", u"6497"); + + assertXPath(pLayout, "//page[3]['pass 1']/infos/bounds", "width", u"6120"); + assertXPath(pLayout, "//page[3]['pass 1']/infos/bounds", "height", u"7937"); + assertXPath(pLayout, "//page[3]['pass 1']/infos/prtBounds", "left", u"720"); + assertXPath(pLayout, "//page[3]['pass 1']/infos/prtBounds", "top", u"720"); + assertXPath(pLayout, "//page[3]['pass 1']/infos/prtBounds", "width", u"5040"); + assertXPath(pLayout, "//page[3]['pass 1']/infos/prtBounds", "height", u"6497"); + } + + saveAndReload(mpFilter); + + { + xmlDocUniquePtr pLayout = parseLayoutDump(); + assertXPath(pLayout, "//page['pass 2']", 3); + + assertXPath(pLayout, "//page[1]['pass 2']/infos/bounds", "width", u"6120"); + assertXPath(pLayout, "//page[1]['pass 2']/infos/bounds", "height", u"7937"); + assertXPath(pLayout, "//page[1]['pass 2']/infos/prtBounds", "left", u"720"); + assertXPath(pLayout, "//page[1]['pass 2']/infos/prtBounds", "top", u"720"); + assertXPath(pLayout, "//page[1]['pass 2']/infos/prtBounds", "width", u"5040"); + assertXPath(pLayout, "//page[1]['pass 2']/infos/prtBounds", "height", u"6497"); + + assertXPath(pLayout, "//page[2]['pass 2']/infos/bounds", "width", u"6120"); + assertXPath(pLayout, "//page[2]['pass 2']/infos/bounds", "height", u"7937"); + assertXPath(pLayout, "//page[2]['pass 2']/infos/prtBounds", "left", u"720"); + assertXPath(pLayout, "//page[2]['pass 2']/infos/prtBounds", "top", u"720"); + assertXPath(pLayout, "//page[2]['pass 2']/infos/prtBounds", "width", u"5040"); + assertXPath(pLayout, "//page[2]['pass 2']/infos/prtBounds", "height", u"6497"); + + assertXPath(pLayout, "//page[3]['pass 2']/infos/bounds", "width", u"6120"); + assertXPath(pLayout, "//page[3]['pass 2']/infos/bounds", "height", u"7937"); + assertXPath(pLayout, "//page[3]['pass 2']/infos/prtBounds", "left", u"720"); + assertXPath(pLayout, "//page[3]['pass 2']/infos/prtBounds", "top", u"720"); + assertXPath(pLayout, "//page[3]['pass 2']/infos/prtBounds", "width", u"5040"); + assertXPath(pLayout, "//page[3]['pass 2']/infos/prtBounds", "height", u"6497"); + } +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx index 9abbeb3dac0b..4c7384697659 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx +++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx @@ -1370,13 +1370,39 @@ void DomainMapper_Impl::PopProperties(ContextType eId) } m_aPropertyStacks[eId].pop(); + { + // !!!FIXME!!! + // We sometimes pop out of order. Just try to comment out this fixing block, keeping the + // assert; and run `make check`. Sigh! In that case, popping from m_aContextStack would + // make it (and m_pTopContext) out-of-sync. This must be an abort; but let's try to fix + // the stack instead, as a temporary (?) measure. I don't replace the stack with a more + // easy-to-manipulate container: I don't optimize for invalid case. + // !!!FIXME!!! + SAL_WARN_IF(m_aContextStack.top() != eId, "writerfilter.dmapper", "pop out of order"); + std::stack<ContextType> tmp; + while (m_aContextStack.top() != eId) + { + tmp.push(m_aContextStack.top()); + m_aContextStack.pop(); + } + m_aContextStack.pop(); + while (!tmp.empty()) + { + m_aContextStack.push(tmp.top()); + tmp.pop(); + } + m_aContextStack.push(eId); // to pop below + } + assert(m_aContextStack.top() == eId); m_aContextStack.pop(); - if(!m_aContextStack.empty() && !m_aPropertyStacks[m_aContextStack.top()].empty()) - - m_pTopContext = m_aPropertyStacks[m_aContextStack.top()].top(); + if (!m_aContextStack.empty() && !m_aPropertyStacks[m_aContextStack.top()].empty()) + { + m_pTopContext = m_aPropertyStacks[m_aContextStack.top()].top(); + } else { - // OSL_ENSURE(eId == CONTEXT_SECTION, "this should happen at a section context end"); + SAL_WARN_IF(eId != CONTEXT_SECTION, "writerfilter.dmapper", + "this should happen at a section context end"); m_pTopContext.clear(); } } commit d1fb9ff22f6bb55482d459e3940ffb6be3be0197 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Sun Jul 27 16:54:43 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Mon Jul 28 16:35:11 2025 +0200 tdf#165564: refactor the "Removes additional spaces in export" part Commit 90ce84a5dd371c804af19e97896a5e19a3ebf8ca (tdf#165564 RTF:Fix export and import of footnotes/endnotes, 2025-05-08) workarounded an extra space added after \super keyword, when EndRunProperties() didn't produce an output. To do that, it introduced a return value from EndRunProperties, checked in TextFootnote_Impl. This change simplifies this, reverting a part of the said commit, which added that return value, in favor of dropping an unnecessary space after {\super, which was retained for some reason in commit 49877dc91b2f91baa656facd462ac1b1e832f182 (sw:rtf - timely export of footnote char properties, 2017-08-24). There is no need in the space between keywords - only between the last keyword and text. Change-Id: If13bf760feb9cb42cb39fc7de39eed6944a869ea Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188432 Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Tested-by: Jenkins diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx index 4472a156a72a..a05f4fe221f7 100644 --- a/sw/source/filter/ww8/attributeoutputbase.hxx +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -160,7 +160,7 @@ public: virtual void StartRunProperties() = 0; /// Called after we end outputting the attributes. - virtual bool EndRunProperties( const SwRedlineData* pRedlineData ) = 0; + virtual void EndRunProperties( const SwRedlineData* pRedlineData ) = 0; /// docx requires footnoteRef/endnoteRef tag at the beginning of each of them virtual bool FootnoteEndnoteRefTag() { return false; }; diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 6d1daf3243cb..156669c65607 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -3687,7 +3687,7 @@ void DocxAttributeOutput::WriteCollectedRunProperties() } } -bool DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) +void DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) { // Call the 'Redline' function. This will add redline (change-tracking) information that regards to run properties. // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc. @@ -3726,7 +3726,6 @@ bool DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) WritePostponedOLE(); WritePostponedActiveXControl(true); - return false; } void DocxAttributeOutput::GetSdtEndBefore(const SdrObject* pSdrObj) diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index af72cd3315d0..5a00c9838b11 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -232,7 +232,7 @@ public: virtual void StartRunProperties() override; /// Called after we end outputting the attributes. - virtual bool EndRunProperties( const SwRedlineData* pRedlineData ) override; + virtual void EndRunProperties( const SwRedlineData* pRedlineData ) override; virtual bool FootnoteEndnoteRefTag() override; diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx index 5c37c5dd3797..2af984b8fc42 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.cxx +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -475,11 +475,10 @@ void RtfAttributeOutput::StartRunProperties() "formatting is not empty"); } -bool RtfAttributeOutput::EndRunProperties(const SwRedlineData* /*pRedlineData*/) +void RtfAttributeOutput::EndRunProperties(const SwRedlineData* /*pRedlineData*/) { const OString aProperties = MoveProperties(ForRun); m_aRun->append(aProperties); - return !aProperties.isEmpty(); } // Very much like AttributeOutputBase::OutputItem @@ -3455,9 +3454,9 @@ void RtfAttributeOutput::TextFootnote_Impl(const SwFormatFootnote& rFootnote) { SAL_INFO("sw.rtf", __func__ << " start"); - m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_SUPER " "); - if (EndRunProperties(nullptr)) - m_aRun->append(' '); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_SUPER); + EndRunProperties(nullptr); + m_aRun->append(' '); WriteTextFootnoteNumStr(rFootnote); m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FOOTNOTE); if (rFootnote.IsEndNote() || m_rExport.m_rDoc.GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER) diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx b/sw/source/filter/ww8/rtfattributeoutput.hxx index 3871b21547ff..289773a1477d 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.hxx +++ b/sw/source/filter/ww8/rtfattributeoutput.hxx @@ -82,8 +82,8 @@ public: /// Called before we start outputting the attributes. void StartRunProperties() override; - /// Called after we end outputting the attributes, returns true if commands were added. - bool EndRunProperties(const SwRedlineData* pRedlineData) override; + /// Called after we end outputting the attributes. + void EndRunProperties(const SwRedlineData* pRedlineData) override; /// Output text (inside a run). void RunText(const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index 3f84782f1dc9..8807c02a4d42 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -1195,7 +1195,7 @@ void WW8AttributeOutput::EndRun( const SwTextNode* /*pNode*/, sal_Int32 nPos, sa } } -bool WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) +void WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) { Redline( pRedlineData ); @@ -1214,7 +1214,6 @@ bool WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() ); } m_rWW8Export.m_pO->clear(); - return false; } void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet, const OUString& /*rSymbolFont*/ ) diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx index 521aed8f8b23..113c086f7828 100644 --- a/sw/source/filter/ww8/ww8attributeoutput.hxx +++ b/sw/source/filter/ww8/ww8attributeoutput.hxx @@ -64,7 +64,7 @@ public: virtual void StartRunProperties() override; /// After we end outputting the attributes. - virtual bool EndRunProperties( const SwRedlineData* pRedlineData ) override; + virtual void EndRunProperties( const SwRedlineData* pRedlineData ) override; /// Output text. virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, const OUString& rSymbolFont = OUString() ) override;