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;