sw/qa/extras/rtfexport/data/tdf167185.rtf | 8 +++ sw/qa/extras/rtfexport/rtfexport8.cxx | 52 ++++++++++++++++++++++ sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx | 26 ++++++----- sw/source/writerfilter/rtftok/rtfsprm.cxx | 6 ++ sw/source/writerfilter/rtftok/rtfsprm.hxx | 3 + 5 files changed, 84 insertions(+), 11 deletions(-)
New commits: commit 07fa13b3319d2d5bc2ddf2f6bdbf5167db0d9936 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Tue Jun 24 16:38:28 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Tue Jun 24 17:22:08 2025 +0200 tdf#167185: make sure that cell width corrections don't accumulate The problem was, that several rows may share the same row definition, and the same RTFSprms in the current state. In commit d00181e8c1ead5020cc8fee47ee7af7fc5039552 (tdf#167169: postpone rleftN processing until ow, 2025-06-23), RTFDocumentImpl::prepareProperties started to modify existing values inside table row sprms in place; and that resulted in accumulating changes, where each next row was getting more corrections of already corrected widths. This change copies the RTFSprms for correction, avoiding the problem. Change-Id: Ia199862c7b8983d93d2fce4898fb60bf8b4a1a4f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186890 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/sw/qa/extras/rtfexport/data/tdf167185.rtf b/sw/qa/extras/rtfexport/data/tdf167185.rtf new file mode 100644 index 000000000000..d0be00a353b0 --- /dev/null +++ b/sw/qa/extras/rtfexport/data/tdf167185.rtf @@ -0,0 +1,8 @@ +{ tf1 + rowd rleft1260+\pard\intbl A1+\pard\intbl A2+\pard\intbl A3+\pard\intbl A4+\pard\intbl A5+} \ No newline at end of file diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx b/sw/qa/extras/rtfexport/rtfexport8.cxx index 90b389e0e1f3..d651d291be75 100644 --- a/sw/qa/extras/rtfexport/rtfexport8.cxx +++ b/sw/qa/extras/rtfexport/rtfexport8.cxx @@ -852,6 +852,58 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167169) } } +CPPUNIT_TEST_FIXTURE(Test, testTdf167185) +{ + // Given a document with a table with a single rleft1260+ // and five rows, all sharing the same row data: + createSwDoc("tdf167185.rtf"); + { + xmlDocUniquePtr pLayout = parseLayoutDump(); + assertXPath(pLayout, "//tab['pass 1']", 1); + assertXPath(pLayout, "//tab['pass 1']/row", 5); + assertXPath(pLayout, "//tab['pass 1']/row[1]/cell", 2); + assertXPath(pLayout, "//tab['pass 1']/row[1]/cell[1]/infos/bounds", "width", u"3119"); + assertXPath(pLayout, "//tab['pass 1']/row[1]/cell[2]/infos/bounds", "width", u"1517"); + assertXPath(pLayout, "//tab['pass 1']/row[2]/cell", 2); + assertXPath(pLayout, "//tab['pass 1']/row[2]/cell[1]/infos/bounds", "width", u"3119"); + assertXPath(pLayout, "//tab['pass 1']/row[2]/cell[2]/infos/bounds", "width", u"1517"); + assertXPath(pLayout, "//tab['pass 1']/row[3]/cell", 2); + assertXPath(pLayout, "//tab['pass 1']/row[3]/cell[1]/infos/bounds", "width", u"3119"); + assertXPath(pLayout, "//tab['pass 1']/row[3]/cell[2]/infos/bounds", "width", u"1517"); + assertXPath(pLayout, "//tab['pass 1']/row[4]/cell", 2); + assertXPath(pLayout, "//tab['pass 1']/row[4]/cell[1]/infos/bounds", "width", u"3119"); + assertXPath(pLayout, "//tab['pass 1']/row[4]/cell[2]/infos/bounds", "width", u"1517"); + assertXPath(pLayout, "//tab['pass 1']/row[5]/cell", 2); + assertXPath(pLayout, "//tab['pass 1']/row[5]/cell[1]/infos/bounds", "width", u"3119"); + assertXPath(pLayout, "//tab['pass 1']/row[5]/cell[2]/infos/bounds", "width", u"1517"); + } + // Check export, too + saveAndReload(mpFilter); + { + xmlDocUniquePtr pLayout = parseLayoutDump(); + assertXPath(pLayout, "//tab['pass 2']", 1); + assertXPath(pLayout, "//tab['pass 2']/row", 5); + assertXPath(pLayout, "//tab['pass 2']/row[1]/cell", 2); + // Rounding (or maybe off-by-one?) errors sadly hit the test + OUString width1 = getXPath(pLayout, "//tab['pass 2']/row[1]/cell[1]/infos/bounds", "width"); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3119, width1.toInt32(), 1); + OUString width2 = getXPath(pLayout, "//tab['pass 2']/row[1]/cell[2]/infos/bounds", "width"); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1517, width2.toInt32(), 1); + assertXPath(pLayout, "//tab['pass 2']/row[2]/cell", 2); + assertXPath(pLayout, "//tab['pass 2']/row[2]/cell[1]/infos/bounds", "width", width1); + assertXPath(pLayout, "//tab['pass 2']/row[2]/cell[2]/infos/bounds", "width", width2); + assertXPath(pLayout, "//tab['pass 2']/row[3]/cell", 2); + assertXPath(pLayout, "//tab['pass 2']/row[3]/cell[1]/infos/bounds", "width", width1); + assertXPath(pLayout, "//tab['pass 2']/row[3]/cell[2]/infos/bounds", "width", width2); + assertXPath(pLayout, "//tab['pass 2']/row[4]/cell", 2); + assertXPath(pLayout, "//tab['pass 2']/row[4]/cell[1]/infos/bounds", "width", width1); + assertXPath(pLayout, "//tab['pass 2']/row[4]/cell[2]/infos/bounds", "width", width2); + assertXPath(pLayout, "//tab['pass 2']/row[5]/cell", 2); + assertXPath(pLayout, "//tab['pass 2']/row[5]/cell[1]/infos/bounds", "width", width1); + assertXPath(pLayout, "//tab['pass 2']/row[5]/cell[2]/infos/bounds", "width", width2); + } +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx b/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx index b7982553bc7c..0109364df396 100644 --- a/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx +++ b/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx @@ -1748,16 +1748,20 @@ void RTFDocumentImpl::prepareProperties( o_rpFrameProperties = new RTFReferenceProperties(RTFSprms(), rState.getFrame().getSprms()); } + // prepareProperties may be called several times for the same rState (once per row); to avoid + // applying the same cell width correction several times, copy TableRowSprms for modification + RTFSprms localTableRowSprms(rState.getTableRowSprms(), RTFSprms::CopyForWrite()); + // Table width. RTFValue::Pointer_t const pTableWidthProps - = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblW); + = localTableRowSprms.find(NS_ooxml::LN_CT_TblPrBase_tblW); if (!pTableWidthProps) { auto pUnitValue = new RTFValue(3); - putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW, + putNestedAttribute(localTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblW, NS_ooxml::LN_CT_TblWidth_type, pUnitValue); auto pWValue = new RTFValue(nCurrentCellX - nTRLeft); - putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW, + putNestedAttribute(localTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblW, NS_ooxml::LN_CT_TblWidth_w, pWValue); } @@ -1765,7 +1769,7 @@ void RTFDocumentImpl::prepareProperties( bool checkedMinusOne = false; bool seenFirstColumn = false; bool seenPositiveWidth = false; - for (auto & [ id, pValue ] : rState.getTableRowSprms()) + for (auto & [ id, pValue ] : localTableRowSprms) { if (id == NS_ooxml::LN_CT_TblGridBase_gridCol) { @@ -1811,17 +1815,17 @@ void RTFDocumentImpl::prepareProperties( if (nTRLeft != 0) { // If there was no tblind, use trleft to set up LN_CT_TblPrBase_tblInd - if (!rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblInd)) + if (!localTableRowSprms.find(NS_ooxml::LN_CT_TblPrBase_tblInd)) { - set_tblInd(rState.getTableRowSprms(), nTRLeft); + set_tblInd(localTableRowSprms, nTRLeft); } } if (nCells > 0) - rState.getTableRowSprms().set(NS_ooxml::LN_tblRow, new RTFValue(1)); + localTableRowSprms.set(NS_ooxml::LN_tblRow, new RTFValue(1)); RTFValue::Pointer_t const pCellMar - = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblCellMar); + = localTableRowSprms.find(NS_ooxml::LN_CT_TblPrBase_tblCellMar); if (!pCellMar) { // If no cell margins are defined, the default left/right margin is 0 in Word, but not in Writer. @@ -1829,14 +1833,14 @@ void RTFDocumentImpl::prepareProperties( aAttributes.set(NS_ooxml::LN_CT_TblWidth_type, new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa)); aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, new RTFValue(0)); - putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar, + putNestedSprm(localTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblCellMar, NS_ooxml::LN_CT_TblCellMar_left, new RTFValue(aAttributes)); - putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar, + putNestedSprm(localTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblCellMar, NS_ooxml::LN_CT_TblCellMar_right, new RTFValue(aAttributes)); } o_rpTableRowProperties - = new RTFReferenceProperties(rState.getTableRowAttributes(), rState.getTableRowSprms()); + = new RTFReferenceProperties(rState.getTableRowAttributes(), localTableRowSprms); } void RTFDocumentImpl::sendProperties( diff --git a/sw/source/writerfilter/rtftok/rtfsprm.cxx b/sw/source/writerfilter/rtftok/rtfsprm.cxx index 8549d54e41e3..830bd3bb8e05 100644 --- a/sw/source/writerfilter/rtftok/rtfsprm.cxx +++ b/sw/source/writerfilter/rtftok/rtfsprm.cxx @@ -481,6 +481,12 @@ RTFSprms::RTFSprms() RTFSprms::~RTFSprms() = default; +RTFSprms::RTFSprms(RTFSprms const& source, CopyForWrite) + : RTFSprms(source) +{ + ensureCopyBeforeWrite(); +} + void RTFSprms::clear() { if (m_pSprms->GetRefCount() == 1) diff --git a/sw/source/writerfilter/rtftok/rtfsprm.hxx b/sw/source/writerfilter/rtftok/rtfsprm.hxx index 275048b4abc1..344c61a42e66 100644 --- a/sw/source/writerfilter/rtftok/rtfsprm.hxx +++ b/sw/source/writerfilter/rtftok/rtfsprm.hxx @@ -45,6 +45,9 @@ public: RTFSprms(); ~RTFSprms() override; + enum class CopyForWrite; + RTFSprms(RTFSprms const& source, CopyForWrite); + RTFSprms(RTFSprms const&) = default; RTFSprms(RTFSprms&&) = default; RTFSprms& operator=(RTFSprms const&) = default;