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 e168463831199305cdfcf6924327af03388c02a5
Author:     Justin Luth <[email protected]>
AuthorDate: Wed Feb 25 19:11:02 2026 -0500
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Mar 2 14:01:37 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/+/200632
    Reviewed-by: Justin Luth <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

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 cc447b90d6c8..76578969857f 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -33,6 +33,23 @@ public:
     }
 };
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf171038_pageAfter)
+{
+    // given a document with a LO-specialty PageAfter break
+    // coupled with !IsPlausableSingleWordSection
+    createSwDoc("tdf171038_pageAfter.docx");
+
+    saveAndReload(u"Office Open XML Text"_ustr);
+
+    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 78f276845a6f..6afd0b16a5fa 100644
--- a/sw/source/filter/ww8/ww8atr.cxx
+++ b/sw/source/filter/ww8/ww8atr.cxx
@@ -4227,16 +4227,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;
         }
@@ -4249,7 +4258,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 )
             {

Reply via email to