sw/qa/extras/ooxmlexport/data/tdf171038_pageAfter.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 17 +++++++++++++++++ sw/source/filter/ww8/ww8atr.cxx | 17 +++++++++++++---- 3 files changed, 30 insertions(+), 4 deletions(-)
New commits: commit 6ff83a91c6a76b335ca8712be00c034a3c6f1179 Author: Justin Luth <[email protected]> AuthorDate: Wed Feb 25 19:11:02 2026 -0500 Commit: Justin Luth <[email protected]> CommitDate: Fri Feb 27 23:34:12 2026 +0100 tdf#171038 docx export: don't duplicate sectPr, surely no extra w:p This fixes creating a document that MS Word calls corrupt. In these particular documents, the page break was specified BOTH by a pageAfter break, and a pageBefore break (on the next para). Because the header has some text, the global tracking flags (m_bParagraphOpened/m_bOpenedParaPr) are reset so "Create a dummy paragraph if needed" ran WHILE ALREADY INSIDE A w:pPr and thus created a corrupt document. Of course, since the sectPr was incorrectly placed, it was ignored by LO anyway - so the page break was also lost. make CppunitTest_sw_ooxmlexport25 \ CPPUNIT_TEST_NAME=testTdf171038_pageAfter Since this code touches DOC and RTF, I also looked at those. DOC seems to be unaffected - it creates sections afterwards. RTF seems to be unaffected - its PrepareNewPageDesc doesn't necessarily insert a section break. In any case, the logic change seems to not be contradictory to their way of exporting. Change-Id: I6c5641cdb98fdc31329ab935ea6407b8083e4065 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200371 Reviewed-by: Justin Luth <[email protected]> Tested-by: Jenkins diff --git a/sw/qa/extras/ooxmlexport/data/tdf171038_pageAfter.docx b/sw/qa/extras/ooxmlexport/data/tdf171038_pageAfter.docx new file mode 100644 index 000000000000..5e4be4004650 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf171038_pageAfter.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx index fab4ac2aef79..a18288e0e137 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx @@ -41,6 +41,23 @@ DECLARE_OOXMLEXPORT_TEST(testTdf148057_columnBreak, "tdf148057_columnBreak.docx" CPPUNIT_ASSERT_EQUAL(2, getPages()); } +CPPUNIT_TEST_FIXTURE(Test, testTdf171038_pageAfter) +{ + // given a document with a LO-specialty PageAfter break + // coupled with !IsPlausableSingleWordSection + createSwDoc("tdf171038_pageAfter.docx"); + + saveAndReload(TestFilter::DOCX); + + CPPUNIT_ASSERT_EQUAL(2, getPages()); + + xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr); + assertXPath(pXmlDoc, "//w:sectPr", 2); // there are two section breaks + assertXPath(pXmlDoc, "//w:body/w:p/w:pPr/w:p/w:pPr/w:sectPr", 0); // would be corrupt document + assertXPath(pXmlDoc, "//w:body/w:p/w:pPr/w:sectPr", 1); // one is in the paragraph rPr + assertXPath(pXmlDoc, "//w:body/w:sectPr", 1); // the other is at the end of the document +} + DECLARE_OOXMLEXPORT_TEST(testTdf166544_noTopMargin_fields, "tdf166544_noTopMargin_fields.docx") { // given a document with a hyperlink field containing a page break diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index 12d10022332a..8a5878dc49e4 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -4239,16 +4239,25 @@ void AttributeOutputBase::FormatBreak( const SvxFormatBreakItem& rBreak ) break; case SvxBreak::PageAfter: case SvxBreak::PageBoth: + { + const WW8_SepInfo* pCurrentSection = GetExport().Sections().CurrentSectionInfo(); + auto pTextNode = dynamic_cast<const SwTextNode*>(GetExport().m_pOutFormatNode); + // If a section break has already been identified/made, then just skip this break. + if (pTextNode && pCurrentSection && pCurrentSection->pPDNd) + { + if (pCurrentSection->pPDNd->GetIndex() > pTextNode->GetIndex()) + return; + } + nC = msword::PageBreak; // #i76300# - check for follow page description, // if current writing attributes of a paragraph. - if ( dynamic_cast< const SwTextNode* >( GetExport().m_pOutFormatNode ) && - GetExport().GetCurItemSet() ) + if (pTextNode && GetExport().GetCurItemSet()) { bCheckForFollowPageDesc = true; } break; - + } default: break; } @@ -4261,7 +4270,7 @@ void AttributeOutputBase::FormatBreak( const SvxFormatBreakItem& rBreak ) { bFollowPageDescWritten = GetExport().OutputFollowPageDesc( GetExport().GetCurItemSet(), - dynamic_cast<const SwTextNode*>( GetExport().m_pOutFormatNode ) ); + static_cast<const SwTextNode*>(GetExport().m_pOutFormatNode)); } if ( !bFollowPageDescWritten ) {
