sw/inc/doc.hxx | 2 - sw/qa/extras/rtfexport/data/section-break-after-section.rtf | 10 ++++++ sw/qa/extras/rtfexport/rtfexport8.cxx | 18 ++++++++++++ sw/source/filter/ww8/rtfattributeoutput.cxx | 7 ++-- sw/source/filter/ww8/rtfexport.cxx | 12 +++++++- 5 files changed, 44 insertions(+), 5 deletions(-)
New commits: commit 264ed42fd66359d8d92f6ef22d9c757514ffb4a3 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Jun 10 09:10:48 2025 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Jun 11 17:35:16 2025 +0200 tdf#166879 RTF export: handle section break right after a section The RTF file opens fine, but saving and opening in either Writer or Word loses the section break between the two paragraphs. What happens is that the first paragraph is in a Writer section, and the second paragraph has an SwFormatPageDesc associated with it. And RtfExport::OutputEndNode() only emits section breaks for end nodes if they are the end of a table. Fix the problem by also handling the end of Writer sections, similar to how it already works for the DOCX export since commit 7060c7b642fdc0a369505e430652ee44205e7eed (tdf#95367 DOCX: allow r-t of changed first/follow sections, 2016-10-10). The original scenario was around saving a file for the 2nd time, the testcase includes a subset of Writer's own RTF export result, so just one import + export is needed. Change-Id: I5cbb9d1ac740158f798fa43c4357c23d8037ba27 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186374 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index fedd24a11311..3c26a31db62e 100644 --- a/sw/inc/doc.hxx +++ b/sw/inc/doc.hxx @@ -1371,7 +1371,7 @@ public: SfxItemSet const*const pAttr, bool const bUpdate = true); static sal_uInt16 IsInsRegionAvailable( const SwPaM& rRange, const SwNode** ppSttNd = nullptr ); - static SwSection* GetCurrSection( const SwPosition& rPos ); + SW_DLLPUBLIC static SwSection* GetCurrSection( const SwPosition& rPos ); SwSectionFormats& GetSections() { return *mpSectionFormatTable; } const SwSectionFormats& GetSections() const { return *mpSectionFormatTable; } SwSectionFormat *MakeSectionFormat(); diff --git a/sw/qa/extras/rtfexport/data/section-break-after-section.rtf b/sw/qa/extras/rtfexport/data/section-break-after-section.rtf new file mode 100644 index 000000000000..26e31d93079f --- /dev/null +++ b/sw/qa/extras/rtfexport/data/section-break-after-section.rtf @@ -0,0 +1,10 @@ +{ tf1 +\paperw11906\paperh16838\margl1588\margr1588\margt1276\margb1276 +\sectd\sbknone +\pard\plain\pagebb +first page\par +\pard\plain +\sect +\sectd \pgwsxn16840\pghsxn11907 +second page\par +} diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx b/sw/qa/extras/rtfexport/rtfexport8.cxx index 5e8ff7b5a112..0cd6317abd6e 100644 --- a/sw/qa/extras/rtfexport/rtfexport8.cxx +++ b/sw/qa/extras/rtfexport/rtfexport8.cxx @@ -482,6 +482,24 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf160976_headerFooter3) verify(); } +CPPUNIT_TEST_FIXTURE(Test, testSectionBreakAfterSection) +{ + // Given a document that is modeled with a Writer section, followed by a paragraph with a new + // page style ("section break"): + createSwDoc("section-break-after-section.rtf"); + + // When saving that document to RTF: + saveAndReload(mpFilter); + + // Then make sure the 2nd paragraph starts on a new page after export, too: + uno::Reference<text::XTextRange> xParagraph = getParagraph(2); + // Without the accompanying fix in place, this test would have failed with: + // - the property is of unexpected type or void: PageDescName + // i.e. the 2nd paragraph was on the same page as the 1st one. + auto aPageDescName = getProperty<OUString>(xParagraph, "PageDescName"); + CPPUNIT_ASSERT(!aPageDescName.isEmpty()); +} + CPPUNIT_TEST_FIXTURE(Test, testTdf158830) { auto verify = [this]() { diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx index 646aabde12f2..ec0f70c1ec9f 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.cxx +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -369,11 +369,12 @@ void RtfAttributeOutput::SectionBreaks(const SwNode& rNode) } else if (rNode.IsEndNode()) { - // End of something: make sure that it's the end of a table. - assert(rNode.StartOfSectionNode()->IsTableNode()); + // End of something: make sure that it's the end of a table or section. + assert(rNode.StartOfSectionNode()->IsTableNode() + || rNode.StartOfSectionNode()->IsSectionNode()); if (aNextIndex.GetNode().IsTextNode()) { - // Handle section break between a table and a text node following it. + // Handle section break between the end node and a text node following it. const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode(); m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode); } diff --git a/sw/source/filter/ww8/rtfexport.cxx b/sw/source/filter/ww8/rtfexport.cxx index e5a078713284..bd9e176a7c5f 100644 --- a/sw/source/filter/ww8/rtfexport.cxx +++ b/sw/source/filter/ww8/rtfexport.cxx @@ -1124,7 +1124,17 @@ bool RtfExport::DisallowInheritingOutlineNumbering(const SwFormat& rFormat) void RtfExport::OutputEndNode(const SwEndNode& rEndNode) { - if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode()) + if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsSectionNode()) + { + // Only consider the end of toplevel sections. + SwPosition aNodePosition(rEndNode); + SwSection* pSect = SwDoc::GetCurrSection(aNodePosition); + if (pSect && pSect->GetParent()) + return; + + AttrOutput().SectionBreaks(rEndNode); + } + else if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode()) // End node of a table: see if a section break should be written after the table. AttrOutput().SectionBreaks(rEndNode); }