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()); }
