sc/qa/unit/data/ods/tdf170963_row_breaks.ods |binary
 sc/qa/unit/subsequent_export_test5.cxx       |   14 ++++++++++++++
 sc/source/filter/excel/xepage.cxx            |   14 +++++++++++---
 3 files changed, 25 insertions(+), 3 deletions(-)

New commits:
commit 63542204016d8f15db4d992ee298037a544b67e2
Author:     Aron Budea <[email protected]>
AuthorDate: Mon Feb 23 04:50:25 2026 +1030
Commit:     Aron Budea <[email protected]>
CommitDate: Fri Feb 27 03:30:51 2026 +0100

    tdf#170963 sc: limit row/colBreaks in XLSX export to what Excel can handle
    
    The number of rowBreaks/colBreaks is practically limited
    to 1023 in Excel, as described in Part 1 Sections
    18.3.1.14 and 18.3.1.74 in [MS-OI29500].
    Don't export more than the limit.
    
    Change-Id: I45e49705022b90ab5b3b404bfc5d07da183e0db1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199999
    Reviewed-by: Aron Budea <[email protected]>
    Tested-by: Jenkins

diff --git a/sc/qa/unit/data/ods/tdf170963_row_breaks.ods 
b/sc/qa/unit/data/ods/tdf170963_row_breaks.ods
new file mode 100644
index 000000000000..ac8befdc7f67
Binary files /dev/null and b/sc/qa/unit/data/ods/tdf170963_row_breaks.ods differ
diff --git a/sc/qa/unit/subsequent_export_test5.cxx 
b/sc/qa/unit/subsequent_export_test5.cxx
index 983b50782898..9d65ce41bf07 100644
--- a/sc/qa/unit/subsequent_export_test5.cxx
+++ b/sc/qa/unit/subsequent_export_test5.cxx
@@ -1453,6 +1453,20 @@ CPPUNIT_TEST_FIXTURE(ScExportTest5, 
testTdf170565_empty_functions)
     assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[6]/x:c[2]/x:f", 
u"PRODUCT(,)");
 }
 
+CPPUNIT_TEST_FIXTURE(ScExportTest5, testTdf170963_row_breaks)
+{
+    createScDoc("ods/tdf170963_row_breaks.ods");
+    save(TestFilter::XLSX);
+
+    xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr);
+    CPPUNIT_ASSERT(pSheet);
+    const int nMaxRowBreaks = 1023;
+    // Original had 1026 row breaks, but the max Excel handles is 1023
+    // Without the fix all 1026 got exported
+    assertXPath(pSheet, "/x:worksheet/x:rowBreaks", "count", 
OUString::number(nMaxRowBreaks));
+    CPPUNIT_ASSERT_EQUAL(nMaxRowBreaks, countXPathNodes(pSheet, 
"/x:worksheet/x:rowBreaks/x:brk"));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepage.cxx 
b/sc/source/filter/excel/xepage.cxx
index 42a579229ec1..dad649ab7256 100644
--- a/sc/source/filter/excel/xepage.cxx
+++ b/sc/source/filter/excel/xepage.cxx
@@ -200,14 +200,22 @@ void XclExpPageBreaks::SaveXml( XclExpXmlStream& rStrm )
 
     sal_Int32 nElement = GetRecId() == EXC_ID_HORPAGEBREAKS ? XML_rowBreaks : 
XML_colBreaks;
     sax_fastparser::FSHelperPtr& pWorksheet = rStrm.GetCurrentStream();
-    OString sNumPageBreaks = OString::number(  mrPageBreaks.size() );
+    // According to Part 1 Sections 18.3.1.14 and 18.3.1.74 in [MS-OI29500], 
the practical
+    // limit is 1023
+    const size_t nMaxPageBreaks = static_cast< size_t >( 1023 );
+    const size_t nNumPageBreaks = std::min( nMaxPageBreaks, 
mrPageBreaks.size() );
+    OString sNumPageBreaks = OString::number( nNumPageBreaks );
+    SAL_WARN_IF( nNumPageBreaks < mrPageBreaks.size(), "sc.filter",
+                 "Number of page breaks exceeds Office implementation maximum, 
limiting them to "
+                    + sNumPageBreaks + " and discarding the rest (total no. 
was "
+                    + OString::number(mrPageBreaks.size()) + ")" );
     pWorksheet->startElement( nElement,
             XML_count,              sNumPageBreaks,
             XML_manualBreakCount,   sNumPageBreaks );
-    for( const auto& rPageBreak : mrPageBreaks )
+    for( size_t nBreak = 0; nBreak < nNumPageBreaks; nBreak++ )
     {
         pWorksheet->singleElement( XML_brk,
-                XML_id,     OString::number(rPageBreak),
+                XML_id,     OString::number(mrPageBreaks[nBreak]),
                 XML_man,    "true",
                 XML_max,    OString::number(mnMaxPos),
                 XML_min,    "0"

Reply via email to