sw/qa/extras/ooxmlexport/data/tdf170389_manyTabstops.odt |binary
 sw/qa/extras/ooxmlexport/ooxmlexport25.cxx               |   12 ++++++++++++
 sw/source/filter/ww8/docxattributeoutput.cxx             |   15 ++++++++++++++-
 3 files changed, 26 insertions(+), 1 deletion(-)

New commits:
commit 34a977af1ef7ae48604a879bf3a47b4d03b0c2c9
Author:     [email protected] <[email protected]>
AuthorDate: Mon Jan 19 11:34:38 2026 -0500
Commit:     Justin Luth <[email protected]>
CommitDate: Thu Jan 22 14:08:55 2026 +0100

    tdf#170389 docx export: limit w:tabs to 64 entries
    
    Files with more than 64 tabstops are reported as corrupt
    by MS Word.
    
    Exposed by 6.2 commit 2bc84658cce1df5050fe788dd0c8a0906a1ca2c3
    Author: Justin Luth on Wed Jul 18 07:37:41 2018 +0200
        related tdf#63561 docx: styles inherit tabstops too
        Reviewed-on: https://gerrit.libreoffice.org/57278
    
    Probably dedup needs to happen here,
    since AFAICS we accumulate the inherited tabs
    in each successive inherted style/paragraph.
    
    make CppunitTest_sw_ooxmlexport25 \
        CPPUNIT_TEST_NAME=testTdf170389_manyTabstops
    
    Change-Id: I2fa356184e5322f9483678c9a50ae6ce96599920
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197606
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>
    (cherry picked from commit f8b72d104ad32171ab637fc83c6f942a930b69e1)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197780

diff --git a/sw/qa/extras/ooxmlexport/data/tdf170389_manyTabstops.odt 
b/sw/qa/extras/ooxmlexport/data/tdf170389_manyTabstops.odt
new file mode 100644
index 000000000000..a54dba2ccf8b
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf170389_manyTabstops.odt differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 8b47bd172393..f6feec1fa36a 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -147,6 +147,18 @@ DECLARE_OOXMLEXPORT_TEST(testTdf165478_bottomAligned, 
"tdf165478_bottomAligned.d
     CPPUNIT_ASSERT_EQUAL(sal_Int32(1887), nFlyTop);
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf170389_manyTabstops)
+{
+    createSwDoc("tdf170389_manyTabstops.odt");
+
+    save(TestFilter::DOCX);
+
+    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+    // MS Word reports document as corrupt if it has more than 64 tabstops 
defined
+    // The paragraph itself defines 40, and inherits 40. Without the fix, this 
was 80
+    assertXPath(pXmlDoc, "//w:tabs/w:tab", 64);
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testInvalidDatetimeInProps)
 {
     createSwDoc("invalidDatetimeInProps.fodt");
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 0a2e440eb2c4..9398166c8c8a 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -9278,6 +9278,9 @@ void DocxAttributeOutput::ParaTabStop( const 
SvxTabStopItem& rTabStop )
 
     m_pSerializer->startElementNS(XML_w, XML_tabs);
 
+    // <w:tabs> may contain 64 <w:tab> entries at most, or else MS Word 
reports the file as corrupt
+    sal_uInt32 nWrittenTabs = 0;
+
     // Get offset for tabs
     // In DOCX, w:pos specifies the position of the current custom tab stop 
with respect to the current page margins.
     // But in ODT, zero position could be page margins or paragraph indent 
according to used settings.
@@ -9287,6 +9290,9 @@ void DocxAttributeOutput::ParaTabStop( const 
SvxTabStopItem& rTabStop )
     sal_Int32 nCurrTab = 0;
     for ( sal_uInt16 i = 0; i < nInheritedTabCount; ++i )
     {
+        if (nWrittenTabs == 64)
+            break; // maximum allowed number of entries reached
+
         while ( nCurrTab < nCount && rTabStop[nCurrTab] < 
pInheritedTabs->At(i) )
             ++nCurrTab;
 
@@ -9295,13 +9301,20 @@ void DocxAttributeOutput::ParaTabStop( const 
SvxTabStopItem& rTabStop )
             m_pSerializer->singleElementNS( XML_w, XML_tab,
                 FSNS( XML_w, XML_val ), "clear",
                 FSNS( XML_w, XML_pos ), 
OString::number(pInheritedTabs->At(i).GetTabPos()) );
+            ++nWrittenTabs;
         }
     }
 
     for (sal_uInt16 i = 0; i < nCount; i++ )
     {
         if( rTabStop[i].GetAdjustment() != SvxTabAdjust::Default )
-            impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset );
+        {
+            if (nWrittenTabs < 64)
+            {
+                impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset );
+                ++nWrittenTabs;
+            }
+        }
         else
             GetExport().setDefaultTabStop( rTabStop[i].GetTabPos());
     }

Reply via email to