sw/qa/extras/rtfexport/data/tdf121493.rtf           |   21 ++++
 sw/qa/extras/rtfexport/data/tdf167169.rtf           |   29 +++++
 sw/qa/extras/rtfexport/rtfexport8.cxx               |  103 ++++++++++++++++++++
 sw/source/writerfilter/rtftok/rtfdispatchsymbol.cxx |    4 
 sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx  |   63 +-----------
 sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx   |   79 ++++++++++++++-
 sw/source/writerfilter/rtftok/rtfdocumentimpl.hxx   |    3 
 7 files changed, 243 insertions(+), 59 deletions(-)

New commits:
commit 23e0ae1273bbba44c155e01315ab81121275cab8
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Mon Jun 23 09:54:48 2025 +0500
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Thu Jun 26 18:03:36 2025 +0200

    tdf#167169: postpone        rleftN processing until  ow
    
    ... where all the data: all     The         blind establishes the left 
indent;      rleft establishes what X
    coordinate will be at the leftmost table edge (i.e.,        blindN tells
    "shift table N twips to the right";         rleftN tells "assume that the
    coordinate of this table's leftmost position is X"). Only when there
    is no       blindN, the     rleftN also defines an indent. Handling all of
    that correctly is only possible at  ow handler; especially because
    the RTF standard tells:
    
      <row> (<tbldef> <cell>+ <tbldef>  ow) | (<tbldef> <cell>+  ow) | (<cell>+ 
<tbldef>  ow)
    
      ...
    
      While Word 97 emitted the row properties (<tbldef>) at the beginning
      of the row, a reader should not assume that this is the case.
      Properties can be emitted at the end, and, in fact, Word 2002, Word
      2003, and Word 2007 do this.
    
    Note: there is a problem of inconsistencies in the documentation and
    implementation of   blindN wrt. units: there is     blindtypeN, which
    should define units used in         blindN, and it is documented to use 0
    for "auto", 1 for "dxa" (=twips), 2 for "no width", and 3 for "pct"
    (fiftieths of a percent). However, in my testing, Word considered 3
    as twips, and ignored       blindN when     blindtypeN had other values.
    In Writer,  blindtypeN is not handled. This needs a separate fix.
    
    Change-Id: If5c47571ca005b248d72ac7ab688c358f4f18e52
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186807
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Tested-by: Jenkins
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186939

diff --git a/sw/qa/extras/rtfexport/data/tdf167169.rtf 
b/sw/qa/extras/rtfexport/data/tdf167169.rtf
new file mode 100644
index 000000000000..6b14ea4f044d
--- /dev/null
+++ b/sw/qa/extras/rtfexport/data/tdf167169.rtf
@@ -0,0 +1,29 @@
+{ tf1
+{      rowd    blind1000       blindtype3+A1: tblind1000 tblindtype3
++B1
++ ow}
+\par
+{      rowd    rleft500        blind1000       blindtype3+A1: trleft500 
tblind1000 tblindtype3
++B1
++ ow}
+\par
+{      rowd    rleft1000       blind1000       blindtype3+A1: trleft1000 
tblind1000 tblindtype3
++B1
++ ow}
+\par
+{      rowd    rleft1500       blind1000       blindtype3+A1: trleft1500 
tblind1000 tblindtype3
++B1
++ ow}
+}
\ No newline at end of file
diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx 
b/sw/qa/extras/rtfexport/rtfexport8.cxx
index 5675cf61910b..90b389e0e1f3 100644
--- a/sw/qa/extras/rtfexport/rtfexport8.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport8.cxx
@@ -795,6 +795,63 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf121493)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf167169)
+{
+    // Given a document with four tables, having different values for tblindN 
and trleftN:
+    createSwDoc("tdf167169.rtf");
+    {
+        xmlDocUniquePtr pLayout = parseLayoutDump();
+        assertXPath(pLayout, "//tab", 4);
+        // 1st table, having   blind1000       blindtype3+        
assertXPath(pLayout, "//tab[1]['pass 1']/infos/prtBounds", "left", u"1000");
+        assertXPath(pLayout, "//tab[1]['pass 1']/row/cell[1]/infos/bounds", 
"width", u"8000");
+        assertXPath(pLayout, "//tab[1]['pass 1']/row/cell[2]/infos/bounds", 
"width", u"1000");
+        // 2nd table, having   rleft500        blind1000       blindtype3+     
   assertXPath(pLayout, "//tab[2]['pass 1']/infos/prtBounds", "left", u"1000");
+        assertXPath(pLayout, "//tab[2]['pass 1']/row/cell[1]/infos/bounds", 
"width", u"7500");
+        assertXPath(pLayout, "//tab[2]['pass 1']/row/cell[2]/infos/bounds", 
"width", u"1000");
+        // 3rd table, having   rleft1000       blind1000       blindtype3+     
   assertXPath(pLayout, "//tab[3]['pass 1']/infos/prtBounds", "left", u"1000");
+        assertXPath(pLayout, "//tab[3]['pass 1']/row/cell[1]/infos/bounds", 
"width", u"7000");
+        assertXPath(pLayout, "//tab[3]['pass 1']/row/cell[2]/infos/bounds", 
"width", u"1000");
+        // 4th table, having   rleft1500       blind1000       blindtype3+     
   assertXPath(pLayout, "//tab[4]['pass 1']/infos/prtBounds", "left", u"1000");
+        assertXPath(pLayout, "//tab[4]['pass 1']/row/cell[1]/infos/bounds", 
"width", u"6500");
+        assertXPath(pLayout, "//tab[4]['pass 1']/row/cell[2]/infos/bounds", 
"width", u"1000");
+    }
+    // Check export, too
+    saveAndReload(mpFilter);
+    {
+        xmlDocUniquePtr pLayout = parseLayoutDump();
+        assertXPath(pLayout, "//tab", 4);
+        // Rounding (or maybe off-by-one?) errors sadly hit the test
+        // 1st table
+        assertXPath(pLayout, "//tab[1]['pass 2']/infos/prtBounds", "left", 
u"1000");
+        OUString width = getXPath(pLayout, "//tab[1]['pass 
2']/row/cell[1]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(8000, width.toInt32(), 1);
+        width = getXPath(pLayout, "//tab[1]['pass 
2']/row/cell[2]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(1000, width.toInt32(), 1);
+        // 2nd table
+        assertXPath(pLayout, "//tab[2]['pass 2']/infos/prtBounds", "left", 
u"1000");
+        width = getXPath(pLayout, "//tab[2]['pass 
2']/row/cell[1]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(7500, width.toInt32(), 1);
+        width = getXPath(pLayout, "//tab[2]['pass 
2']/row/cell[2]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(1000, width.toInt32(), 1);
+        // 3rd table
+        assertXPath(pLayout, "//tab[3]['pass 2']/infos/prtBounds", "left", 
u"1000");
+        width = getXPath(pLayout, "//tab[3]['pass 
2']/row/cell[1]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(7000, width.toInt32(), 1);
+        width = getXPath(pLayout, "//tab[3]['pass 
2']/row/cell[2]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(1000, width.toInt32(), 1);
+        // 4th table
+        assertXPath(pLayout, "//tab[4]['pass 2']/infos/prtBounds", "left", 
u"1000");
+        width = getXPath(pLayout, "//tab[4]['pass 
2']/row/cell[1]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(6500, width.toInt32(), 1);
+        width = getXPath(pLayout, "//tab[4]['pass 
2']/row/cell[2]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(1000, width.toInt32(), 1);
+    }
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/writerfilter/rtftok/rtfdispatchsymbol.cxx 
b/sw/source/writerfilter/rtftok/rtfdispatchsymbol.cxx
index 4b2e785b2e7a..4ce31f620f1b 100644
--- a/sw/source/writerfilter/rtftok/rtfdispatchsymbol.cxx
+++ b/sw/source/writerfilter/rtftok/rtfdispatchsymbol.cxx
@@ -220,7 +220,7 @@ RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword 
nKeyword)
                                    m_aNestedTableCellsAttributes, 
m_nNestedCells));
             prepareProperties(m_aStates.top(), pBuffer->GetParaProperties(),
                               pBuffer->GetFrameProperties(), 
pBuffer->GetRowProperties(),
-                              m_nNestedCells, m_nNestedCurrentCellX - 
m_nNestedTRLeft);
+                              m_nNestedCells, m_nNestedCurrentCellX, 
m_nNestedTRLeft);
 
             if (m_aTableBufferStack.size() == 1 || 
!m_aStates.top().getCurrentBuffer())
             {
@@ -404,7 +404,7 @@ RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword 
nKeyword)
             writerfilter::Reference<Properties>::Pointer_t frameProperties;
             writerfilter::Reference<Properties>::Pointer_t rowProperties;
             prepareProperties(m_aStates.top(), paraProperties, 
frameProperties, rowProperties,
-                              m_nTopLevelCells, m_nTopLevelCurrentCellX - 
m_nTopLevelTRLeft);
+                              m_nTopLevelCells, m_nTopLevelCurrentCellX, 
m_nTopLevelTRLeft);
             sendProperties(paraProperties, frameProperties, rowProperties);
 
             m_bNeedPap = true;
diff --git a/sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx 
b/sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx
index a5999fbcde25..06cc3adfe0cd 100644
--- a/sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx
+++ b/sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx
@@ -386,32 +386,6 @@ bool RTFDocumentImpl::dispatchFrameValue(RTFKeyword 
nKeyword, int nParam)
     return false;
 }
 
-static int GetCellWidth(int thisCellX, int prevCellX, RTFSprms& tableRowSprms)
-{
-    thisCellX -= prevCellX;
-    if (thisCellX == 0 && prevCellX > 0)
-    {
-        // If width of cell is 0, BUT there is a value for -        // 
possible width. But if -        // try to resolve this.
-
-        // sw/source/filter/inc/wrtswtbl.hxx, minimal possible width of cells.
-        const int COL_DFLT_WIDTH = 41;
-        thisCellX = COL_DFLT_WIDTH;
-    }
-    // If there is a negative left margin, then the first cellx is relative to 
that.
-    if (prevCellX == 0)
-    {
-        if (RTFValue::Pointer_t pTblInd = 
tableRowSprms.find(NS_ooxml::LN_CT_TblPrBase_tblInd))
-        {
-            RTFValue::Pointer_t pWidth = 
pTblInd->getAttributes().find(NS_ooxml::LN_CT_TblWidth_w);
-            if (pWidth && pWidth->getInt() < 0)
-                thisCellX = -1 * (pWidth->getInt() - prevCellX);
-        }
-    }
-    return thisCellX;
-}
-
 bool RTFDocumentImpl::dispatchTableValue(RTFKeyword nKeyword, int nParam)
 {
     int nSprm = 0;
@@ -425,9 +399,9 @@ bool RTFDocumentImpl::dispatchTableValue(RTFKeyword 
nKeyword, int nParam)
                 (Destination::NESTEDTABLEPROPERTIES == 
m_aStates.top().getDestination())
                     ? m_nNestedCurrentCellX
                     : m_nTopLevelCurrentCellX);
-            int nCellX = GetCellWidth(nParam, rCurrentCellX, 
m_aStates.top().getTableRowSprms());
+            int nCellWidth = nParam - rCurrentCellX;
             rCurrentCellX = nParam;
-            auto pXValue = new RTFValue(nCellX);
+            auto pXValue = new RTFValue(nCellWidth);
             
m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, 
pXValue,
                                                    RTFOverwrite::NO_APPEND);
             if (Destination::NESTEDTABLEPROPERTIES == 
m_aStates.top().getDestination())
@@ -488,60 +462,18 @@ bool RTFDocumentImpl::dispatchTableValue(RTFKeyword 
nKeyword, int nParam)
         }
         break;
         case RTFKeyword::TRLEFT:
-        case RTFKeyword::TBLIND:
         {
-            // the value is in twips
             auto const aDestination = m_aStates.top().getDestination();
             int& rCurrentTRLeft((Destination::NESTEDTABLEPROPERTIES == 
aDestination)
                                     ? m_nNestedTRLeft
                                     : m_nTopLevelTRLeft);
-            int& rCurrentCellX((Destination::NESTEDTABLEPROPERTIES == 
aDestination)
-                                   ? m_nNestedCurrentCellX
-                                   : m_nTopLevelCurrentCellX);
-            putNestedAttribute(m_aStates.top().getTableRowSprms(), 
NS_ooxml::LN_CT_TblPrBase_tblInd,
-                               NS_ooxml::LN_CT_TblWidth_type,
-                               new 
RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa));
-
-            if (nKeyword == RTFKeyword::TBLIND)
-            {
-                RTFValue::Pointer_t pCellMargin
-                    = 
m_aStates.top().getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblCellMar);
-                if (pCellMargin)
-                {
-                    RTFValue::Pointer_t pMarginLeft
-                        = 
pCellMargin->getSprms().find(NS_ooxml::LN_CT_TcMar_left);
-                    if (pMarginLeft)
-                        nParam -= pMarginLeft->getAttributes()
-                                      .find(NS_ooxml::LN_CT_TblWidth_w)
-                                      ->getInt();
-                }
-            }
             rCurrentTRLeft = nParam;
-            // Correct the first cellX, if already pushed before these.
-            // FIXME: this whole convoluted processing of CELLX, TRLEFT, 
TBLIND should be replaced
-            // with simple pushing of the respective values as is; and all 
that should eventually
-            // be processed in RTFKeyword::ROW handler 
(RTFDocumentImpl::dispatchSymbol), where all
-            // information would already be available. There we could know the 
table indent, row
-            // left offset, all right cell boundaries; and could calculate 
correct widths (likely
-            // in prepareProperties call).
-            bool hadCellX = false;
-            for (auto & [ id, pValue ] : m_aStates.top().getTableRowSprms())
-            {
-                if (id == NS_ooxml::LN_CT_TblGridBase_gridCol)
-                {
-                    if (int val = pValue->getInt(); val != -1)
-                    {
-                        val = GetCellWidth(val, nParam, 
m_aStates.top().getTableRowSprms());
-                        pValue = new RTFValue(val);
-                        hadCellX = true;
-                        break;
-                    }
-                }
-            }
-            if (!hadCellX)
-                rCurrentCellX = rCurrentTRLeft;
-            putNestedAttribute(m_aStates.top().getTableRowSprms(), 
NS_ooxml::LN_CT_TblPrBase_tblInd,
-                               +NS_ooxml::LN_CT_TblWidth_w, new 
RTFValue(nParam));
+            return true;
+        }
+        break;
+        case RTFKeyword::TBLIND:
+        {
+            set_tblInd(m_aStates.top().getTableRowSprms(), nParam);
             return true;
         }
         break;
diff --git a/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx 
b/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx
index d1b72e0a27ad..61c52ad205da 100644
--- a/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx
+++ b/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx
@@ -1713,11 +1713,30 @@ void RTFDocumentImpl::text(OUString& rString)
     }
 }
 
+void RTFDocumentImpl::set_tblInd(RTFSprms& tableRowSprms, int val)
+{
+    // the value is in twips
+    putNestedAttribute(tableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblInd,
+                       NS_ooxml::LN_CT_TblWidth_type,
+                       new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa));
+
+    RTFValue::Pointer_t pCellMargin = 
tableRowSprms.find(NS_ooxml::LN_CT_TblPrBase_tblCellMar);
+    if (pCellMargin)
+    {
+        RTFValue::Pointer_t pMarginLeft = 
pCellMargin->getSprms().find(NS_ooxml::LN_CT_TcMar_left);
+        if (pMarginLeft)
+            val -= 
pMarginLeft->getAttributes().find(NS_ooxml::LN_CT_TblWidth_w)->getInt();
+    }
+
+    putNestedAttribute(tableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblInd, 
+NS_ooxml::LN_CT_TblWidth_w,
+                       new RTFValue(val));
+}
+
 void RTFDocumentImpl::prepareProperties(
     RTFParserState& rState, writerfilter::Reference<Properties>::Pointer_t& 
o_rpParagraphProperties,
     writerfilter::Reference<Properties>::Pointer_t& o_rpFrameProperties,
     writerfilter::Reference<Properties>::Pointer_t& o_rpTableRowProperties, 
int const nCells,
-    int const nCurrentCellX)
+    int const nCurrentCellX, int nTRLeft)
 {
     o_rpParagraphProperties
         = getProperties(rState.getParagraphAttributes(), 
rState.getParagraphSprms(),
@@ -1736,11 +1755,67 @@ void RTFDocumentImpl::prepareProperties(
         auto pUnitValue = new RTFValue(3);
         putNestedAttribute(rState.getTableRowSprms(), 
NS_ooxml::LN_CT_TblPrBase_tblW,
                            NS_ooxml::LN_CT_TblWidth_type, pUnitValue);
-        auto pWValue = new RTFValue(nCurrentCellX);
+        auto pWValue = new RTFValue(nCurrentCellX - nTRLeft);
         putNestedAttribute(rState.getTableRowSprms(), 
NS_ooxml::LN_CT_TblPrBase_tblW,
                            NS_ooxml::LN_CT_TblWidth_w, pWValue);
     }
 
+    // Correct cells' widths.
+    bool checkedMinusOne = false;
+    bool seenFirstColumn = false;
+    bool seenPositiveWidth = false;
+    for (auto & [ id, pValue ] : rState.getTableRowSprms())
+    {
+        if (id == NS_ooxml::LN_CT_TblGridBase_gridCol)
+        {
+            int val = pValue->getInt();
+            if (!checkedMinusOne)
+            {
+                // -1 is the special value set in 
RTFDocumentImpl::resetTableRowProperties
+                // and used in DomainMapperTableManager::sprm; skip it
+                checkedMinusOne = true;
+                if (val == -1)
+                    continue;
+            }
+            if (!seenFirstColumn)
+            {
+                if (nTRLeft != 0)
+                {
+                    // First cell: it was calculated against the initial value 
of *CurrentCellX,
+                    // which is 0; now subtract nTRLeft from it
+                    val -= nTRLeft;
+                    pValue = new RTFValue(val);
+                }
+                seenFirstColumn = true;
+                if (val > 0)
+                    seenPositiveWidth = true;
+                continue;
+            }
+            if (val > 0)
+            {
+                seenPositiveWidth = true;
+                continue;
+            }
+            // If width of this cell, and all previous cells, is 0, leave 0 so 
autofit will try
+            // to resolve this. But when there were proper widths before, use 
minimal width.
+            if (!seenPositiveWidth)
+                continue;
+
+            // sw/source/filter/inc/wrtswtbl.hxx, minimal possible width of 
cells.
+            const int COL_DFLT_WIDTH = 41;
+            pValue = new RTFValue(COL_DFLT_WIDTH);
+        }
+    }
+
+    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))
+        {
+            set_tblInd(rState.getTableRowSprms(), nTRLeft);
+        }
+    }
+
     if (nCells > 0)
         rState.getTableRowSprms().set(NS_ooxml::LN_tblRow, new RTFValue(1));
 
diff --git a/sw/source/writerfilter/rtftok/rtfdocumentimpl.hxx 
b/sw/source/writerfilter/rtftok/rtfdocumentimpl.hxx
index fe0a960c8fb0..12e4aa64d151 100644
--- a/sw/source/writerfilter/rtftok/rtfdocumentimpl.hxx
+++ b/sw/source/writerfilter/rtftok/rtfdocumentimpl.hxx
@@ -791,11 +791,12 @@ private:
     void checkNeedPap();
     void handleFontTableEntry();
     void sectBreak(bool bFinal = false);
+    static void set_tblInd(RTFSprms& tableRowSprms, int val);
     void prepareProperties(RTFParserState& rState,
                            writerfilter::Reference<Properties>::Pointer_t& 
o_rpParagraphProperties,
                            writerfilter::Reference<Properties>::Pointer_t& 
o_rpFrameProperties,
                            writerfilter::Reference<Properties>::Pointer_t& 
o_rpTableRowProperties,
-                           int nCells, int nCurrentCellX);
+                           int nCells, int nCurrentCellX, int nTRLeft);
     /// Send the passed properties to dmapper.
     void sendProperties(writerfilter::Reference<Properties>::Pointer_t const& 
pParagraphProperties,
                         writerfilter::Reference<Properties>::Pointer_t const& 
pFrameProperties,
commit 922fc46fce72d6d4f3d12cfe92e23adc16ddb13c
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sun Jun 22 02:49:05 2025 +0500
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Thu Jun 26 18:03:22 2025 +0200

    tdf#121493: handle  rleftN after     
    I took an easy path, and just fixed the possible pre-existing value
    when        rleftN is handled. But the proper fix would be to avoid the
    cell widths calculations altogether, until  ow is handled. At that
    point, all information is already gathered.
    
    Search for the first instance of LN_CT_TblGridBase_gridCol, that is
    not -1 (the special value inserted in 
RTFDocumentImpl::resetTableRowProperties,
    and used in DomainMapperTableManager::sprm); and if found, correct
    it by the calculated TRLeft value.
    When there was no previous     
    Change-Id: Ie6cf64d594e166abb1ed3e939207101d96f75c63
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186790
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186938

diff --git a/sw/qa/extras/rtfexport/data/tdf121493.rtf 
b/sw/qa/extras/rtfexport/data/tdf121493.rtf
new file mode 100644
index 000000000000..79f115430deb
--- /dev/null
+++ b/sw/qa/extras/rtfexport/data/tdf121493.rtf
@@ -0,0 +1,21 @@
+{ tf1
+{      rowd+aaa
++bbb
++ ow}
+\par
+{      rowd    rleft1000+aaa
++bbb
++ ow}
+\par
+{      rowd    rleft0+aaa
++bbb
++ ow}}
\ No newline at end of file
diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx 
b/sw/qa/extras/rtfexport/rtfexport8.cxx
index f255fbde2a20..5675cf61910b 100644
--- a/sw/qa/extras/rtfexport/rtfexport8.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport8.cxx
@@ -12,6 +12,7 @@
 #include <com/sun/star/awt/FontWeight.hpp>
 #include <com/sun/star/awt/Gradient2.hpp>
 #include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/table/XTableColumns.hpp>
 #include <com/sun/star/text/GraphicCrop.hpp>
 #include <com/sun/star/text/XFootnote.hpp>
 #include <com/sun/star/text/XFootnotesSupplier.hpp>
@@ -749,6 +750,51 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf155835)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf121493)
+{
+    // Given a document with three tables, having different order of a row's 
cellxN and trleftN:
+    createSwDoc("tdf121493.rtf");
+    {
+        xmlDocUniquePtr pLayout = parseLayoutDump();
+        // 1st table, having +        assertXPath(pLayout, "//tab[1]['pass 
1']/infos/prtBounds", "left", u"1000");
+        assertXPath(pLayout, "//tab[1]['pass 1']/row/cell[1]/infos/bounds", 
"width", u"7000");
+        assertXPath(pLayout, "//tab[1]['pass 1']/row/cell[2]/infos/bounds", 
"width", u"1000");
+        // 2nd table, having   rleft1000+        assertXPath(pLayout, 
"//tab[2]['pass 1']/infos/prtBounds", "left", u"1000");
+        assertXPath(pLayout, "//tab[2]['pass 1']/row/cell[1]/infos/bounds", 
"width", u"7000");
+        assertXPath(pLayout, "//tab[2]['pass 1']/row/cell[2]/infos/bounds", 
"width", u"1000");
+        // 3rd table, having   rleft0+        assertXPath(pLayout, 
"//tab[3]['pass 1']/infos/prtBounds", "left", u"0");
+        assertXPath(pLayout, "//tab[3]['pass 1']/row/cell[1]/infos/bounds", 
"width", u"8000");
+        assertXPath(pLayout, "//tab[3]['pass 1']/row/cell[2]/infos/bounds", 
"width", u"1000");
+    }
+    // Check export, too
+    saveAndReload(mpFilter);
+    {
+        xmlDocUniquePtr pLayout = parseLayoutDump();
+        // Rounding (or maybe off-by-one?) errors sadly hit the test
+        // 1st table
+        assertXPath(pLayout, "//tab[1]['pass 2']/infos/prtBounds", "left", 
u"1000");
+        OUString width = getXPath(pLayout, "//tab[1]['pass 
2']/row/cell[1]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(7000, width.toInt32(), 1);
+        width = getXPath(pLayout, "//tab[1]['pass 
2']/row/cell[2]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(1000, width.toInt32(), 1);
+        // 2nd table
+        assertXPath(pLayout, "//tab[2]['pass 2']/infos/prtBounds", "left", 
u"1000");
+        width = getXPath(pLayout, "//tab[2]['pass 
2']/row/cell[1]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(7000, width.toInt32(), 1);
+        width = getXPath(pLayout, "//tab[2]['pass 
2']/row/cell[2]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(1000, width.toInt32(), 1);
+        // 3rd table
+        assertXPath(pLayout, "//tab[3]['pass 2']/infos/prtBounds", "left", 
u"0");
+        width = getXPath(pLayout, "//tab[3]['pass 
2']/row/cell[1]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(8000, width.toInt32(), 1);
+        width = getXPath(pLayout, "//tab[3]['pass 
2']/row/cell[2]/infos/bounds", "width");
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(1000, width.toInt32(), 1);
+    }
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx 
b/sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx
index d89c3420017a..a5999fbcde25 100644
--- a/sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx
+++ b/sw/source/writerfilter/rtftok/rtfdispatchvalue.cxx
@@ -386,6 +386,32 @@ bool RTFDocumentImpl::dispatchFrameValue(RTFKeyword 
nKeyword, int nParam)
     return false;
 }
 
+static int GetCellWidth(int thisCellX, int prevCellX, RTFSprms& tableRowSprms)
+{
+    thisCellX -= prevCellX;
+    if (thisCellX == 0 && prevCellX > 0)
+    {
+        // If width of cell is 0, BUT there is a value for +        // 
possible width. But if +        // try to resolve this.
+
+        // sw/source/filter/inc/wrtswtbl.hxx, minimal possible width of cells.
+        const int COL_DFLT_WIDTH = 41;
+        thisCellX = COL_DFLT_WIDTH;
+    }
+    // If there is a negative left margin, then the first cellx is relative to 
that.
+    if (prevCellX == 0)
+    {
+        if (RTFValue::Pointer_t pTblInd = 
tableRowSprms.find(NS_ooxml::LN_CT_TblPrBase_tblInd))
+        {
+            RTFValue::Pointer_t pWidth = 
pTblInd->getAttributes().find(NS_ooxml::LN_CT_TblWidth_w);
+            if (pWidth && pWidth->getInt() < 0)
+                thisCellX = -1 * (pWidth->getInt() - prevCellX);
+        }
+    }
+    return thisCellX;
+}
+
 bool RTFDocumentImpl::dispatchTableValue(RTFKeyword nKeyword, int nParam)
 {
     int nSprm = 0;
@@ -399,30 +425,7 @@ bool RTFDocumentImpl::dispatchTableValue(RTFKeyword 
nKeyword, int nParam)
                 (Destination::NESTEDTABLEPROPERTIES == 
m_aStates.top().getDestination())
                     ? m_nNestedCurrentCellX
                     : m_nTopLevelCurrentCellX);
-            int nCellX = nParam - rCurrentCellX;
-
-            if (!nCellX && nParam > 0)
-            {
-                // If width of cell is 0, BUT there is a value for -           
     // possible width. But if -                // try to resolve this.
-
-                // sw/source/filter/inc/wrtswtbl.hxx, minimal possible width 
of cells.
-                const int COL_DFLT_WIDTH = 41;
-                nCellX = COL_DFLT_WIDTH;
-            }
-
-            // If there is a negative left margin, then the first cellx is 
relative to that.
-            RTFValue::Pointer_t pTblInd
-                = 
m_aStates.top().getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblInd);
-            if (rCurrentCellX == 0 && pTblInd)
-            {
-                RTFValue::Pointer_t pWidth
-                    = 
pTblInd->getAttributes().find(NS_ooxml::LN_CT_TblWidth_w);
-                if (pWidth && pWidth->getInt() < 0)
-                    nCellX = -1 * (pWidth->getInt() - nParam);
-            }
-
+            int nCellX = GetCellWidth(nParam, rCurrentCellX, 
m_aStates.top().getTableRowSprms());
             rCurrentCellX = nParam;
             auto pXValue = new RTFValue(nCellX);
             
m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, 
pXValue,
@@ -512,11 +515,31 @@ bool RTFDocumentImpl::dispatchTableValue(RTFKeyword 
nKeyword, int nParam)
                                       .find(NS_ooxml::LN_CT_TblWidth_w)
                                       ->getInt();
                 }
-                rCurrentTRLeft = nParam;
             }
-            else
-                rCurrentTRLeft = rCurrentCellX = nParam;
-
+            rCurrentTRLeft = nParam;
+            // Correct the first cellX, if already pushed before these.
+            // FIXME: this whole convoluted processing of CELLX, TRLEFT, 
TBLIND should be replaced
+            // with simple pushing of the respective values as is; and all 
that should eventually
+            // be processed in RTFKeyword::ROW handler 
(RTFDocumentImpl::dispatchSymbol), where all
+            // information would already be available. There we could know the 
table indent, row
+            // left offset, all right cell boundaries; and could calculate 
correct widths (likely
+            // in prepareProperties call).
+            bool hadCellX = false;
+            for (auto & [ id, pValue ] : m_aStates.top().getTableRowSprms())
+            {
+                if (id == NS_ooxml::LN_CT_TblGridBase_gridCol)
+                {
+                    if (int val = pValue->getInt(); val != -1)
+                    {
+                        val = GetCellWidth(val, nParam, 
m_aStates.top().getTableRowSprms());
+                        pValue = new RTFValue(val);
+                        hadCellX = true;
+                        break;
+                    }
+                }
+            }
+            if (!hadCellX)
+                rCurrentCellX = rCurrentTRLeft;
             putNestedAttribute(m_aStates.top().getTableRowSprms(), 
NS_ooxml::LN_CT_TblPrBase_tblInd,
                                +NS_ooxml::LN_CT_TblWidth_w, new 
RTFValue(nParam));
             return true;

Reply via email to