[Libreoffice-commits] core.git: Branch 'distro/collabora/dcm-6.2' - 21 commits - include/svx svx/source sw/qa sw/source writerfilter/source

Miklos Vajna (via logerrit) logerrit at kemper.freedesktop.org
Mon Mar 8 10:57:00 UTC 2021


 include/svx/framelink.hxx                                           |    7 
 svx/source/dialog/framelink.cxx                                     |  117 ++++++
 sw/qa/extras/ooxmlexport/data/tdf116194.docx                        |binary
 sw/qa/extras/ooxmlexport/data/tdf119054.docx                        |binary
 sw/qa/extras/ooxmlexport/data/tdf124399_SingleCellTableBorders.docx |binary
 sw/qa/extras/ooxmlexport/data/tdf127814.docx                        |binary
 sw/qa/extras/ooxmlexport/data/tdf128752.docx                        |binary
 sw/qa/extras/ooxmlexport/data/tdf129442_RightBorder.docx            |binary
 sw/qa/extras/ooxmlexport/data/tdf129450_BottomBorder.docx           |binary
 sw/qa/extras/ooxmlexport/data/tdf129452_BottomBorders.docx          |binary
 sw/qa/extras/ooxmlexport/data/tdf129575-directAfter.docx            |binary
 sw/qa/extras/ooxmlexport/data/tdf129575-directBefore.docx           |binary
 sw/qa/extras/ooxmlexport/data/tdf129575-docDefault.docx             |binary
 sw/qa/extras/ooxmlexport/data/tdf129575-styleAfter.docx             |binary
 sw/qa/extras/ooxmlexport/data/tdf81100.docx                         |binary
 sw/qa/extras/ooxmlexport/ooxmlexport10.cxx                          |   11 
 sw/qa/extras/ooxmlexport/ooxmlexport11.cxx                          |   20 +
 sw/qa/extras/ooxmlexport/ooxmlexport3.cxx                           |   48 ++
 sw/qa/extras/ooxmlexport/ooxmlexport6.cxx                           |   30 +
 sw/qa/extras/ooxmlexport/ooxmlexport8.cxx                           |    6 
 sw/qa/extras/ooxmlexport/ooxmlexport9.cxx                           |   65 +++
 sw/qa/extras/rtfimport/rtfimport.cxx                                |    2 
 sw/source/core/inc/cellfrm.hxx                                      |    6 
 sw/source/core/layout/paintfrm.cxx                                  |   76 ++++
 sw/source/core/layout/tabfrm.cxx                                    |   84 ++++
 sw/source/filter/ww8/docxattributeoutput.cxx                        |    2 
 sw/source/filter/ww8/docxtablestyleexport.cxx                       |   23 +
 writerfilter/source/dmapper/ConversionHelper.cxx                    |    9 
 writerfilter/source/dmapper/ConversionHelper.hxx                    |    1 
 writerfilter/source/dmapper/DomainMapper.cxx                        |    8 
 writerfilter/source/dmapper/DomainMapperTableHandler.cxx            |  181 +++++++---
 writerfilter/source/dmapper/DomainMapperTableHandler.hxx            |    3 
 writerfilter/source/dmapper/DomainMapperTableManager.cxx            |   69 +--
 writerfilter/source/dmapper/DomainMapperTableManager.hxx            |    2 
 writerfilter/source/dmapper/DomainMapper_Impl.cxx                   |   89 +++-
 writerfilter/source/dmapper/DomainMapper_Impl.hxx                   |   19 -
 writerfilter/source/dmapper/PropertyIds.cxx                         |   10 
 writerfilter/source/dmapper/PropertyIds.hxx                         |   62 +--
 writerfilter/source/dmapper/PropertyMap.cxx                         |   28 +
 writerfilter/source/dmapper/PropertyMap.hxx                         |   19 -
 writerfilter/source/dmapper/StyleSheetTable.cxx                     |   22 -
 writerfilter/source/dmapper/StyleSheetTable.hxx                     |    1 
 writerfilter/source/dmapper/TableData.hxx                           |   41 ++
 writerfilter/source/dmapper/TableManager.cxx                        |   76 ++++
 writerfilter/source/dmapper/TableManager.hxx                        |    8 
 writerfilter/source/dmapper/TblStylePrHandler.cxx                   |   12 
 writerfilter/source/ooxml/OOXMLFastContextHandler.cxx               |    4 
 writerfilter/source/ooxml/model.xml                                 |    5 
 48 files changed, 1002 insertions(+), 164 deletions(-)

New commits:
commit 630e1a35d189d07a9bade63279ad179bbcd7318e
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Thu Mar 4 11:52:58 2021 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 17:50:38 2021 +0100

    sw: fix unwanted long vertical border around vertically merged Word cell
    
    In case cells A1 and A2 are vertically merged, they can still specify
    different border properties for the two cells. Word draws the border
    properties of the covered cells, while Writer ignores the formatting of
    A2 in this case and only uses the properties of the covering cell.
    
    Table cell border collapsing rules already differ in Word and Writer, so
    SwTabFramePainter::Insert() knows if the table cell's border is
    Word-style or not. Extend this code to handle vertically merged cells
    better.
    
    In general, this means a cell no longer has a fixed set of 4 borders,
    but each edge may have several sub-borders. This commit is a step in
    that direction, and handles the case when a vertical (left or right)
    border style is set initially, but not set at the end -- as we iterate
    through the list of cells in a vertical merge.
    
    (cherry picked from commit 66ac8e60896f6306bed8fbb34606fd14474f19ce)
    
    Conflicts:
            sw/qa/core/layout/layout.cxx
            sw/source/core/layout/paintfrm.cxx
    
    Change-Id: I35a05097ce70243099307ce8066766aef61a2bc5

diff --git a/sw/source/core/inc/cellfrm.hxx b/sw/source/core/inc/cellfrm.hxx
index 4e293a601f45..894763d410ef 100644
--- a/sw/source/core/inc/cellfrm.hxx
+++ b/sw/source/core/inc/cellfrm.hxx
@@ -59,6 +59,12 @@ public:
     const SwCellFrame& FindStartEndOfRowSpanCell( bool bStart ) const;
     long GetLayoutRowSpan() const;
 
+    /// If this is a vertically merged cell, then looks up its covered cell in rRow.
+    const SwCellFrame* GetCoveredCellInRow(const SwRowFrame& rRow) const;
+
+    /// If this is a vertically merged cell, then looks up its covered cells.
+    std::vector<const SwCellFrame*> GetCoveredCells() const;
+
     void dumpAsXmlAttributes(xmlTextWriterPtr writer) const override;
 };
 
diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx
index fc15487b774c..9d7029f2f19d 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -2213,11 +2213,14 @@ struct SwLineEntry
     SwTwips mnKey;
     SwTwips mnStartPos;
     SwTwips mnEndPos;
+    SwTwips mnLimitedEndPos;
 
     svx::frame::Style maAttribute;
 
     enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };
 
+    enum class VerticalType { LEFT, RIGHT };
+
 public:
     SwLineEntry( SwTwips nKey,
                  SwTwips nStartPos,
@@ -2225,6 +2228,12 @@ public:
                  const svx::frame::Style& rAttribute );
 
     OverlapType Overlaps( const SwLineEntry& rComp ) const;
+
+    /**
+     * Assuming that this entry is for a Word-style covering cell and the border matching eType is
+     * set, limit the end position of this border in case covered cells have no borders set.
+     */
+    void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
 };
 
 SwLineEntry::SwLineEntry( SwTwips nKey,
@@ -2234,6 +2243,7 @@ SwLineEntry::SwLineEntry( SwTwips nKey,
     :   mnKey( nKey ),
         mnStartPos( nStartPos ),
         mnEndPos( nEndPos ),
+        mnLimitedEndPos(0),
         maAttribute( rAttribute )
 {
 }
@@ -2287,6 +2297,37 @@ SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew )  const
     return eRet;
 }
 
+void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
+{
+    if (!rFrame.IsCellFrame())
+    {
+        return;
+    }
+
+    const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
+    std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
+    // Iterate in reverse order, so we can stop at the first cell that has a border. This can
+    // determine what is the minimal end position that is safe to use as a limit.
+    for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
+    {
+        const SwCellFrame* pCoveredCell = *it;
+        SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
+        const SwBorderAttrs& rAttrs = *aAccess.Get();
+        const SvxBoxItem& rBox = rAttrs.GetBox();
+        if (eType == VerticalType::LEFT && rBox.GetLeft())
+        {
+            break;
+        }
+
+        if (eType == VerticalType::RIGHT && rBox.GetRight())
+        {
+            break;
+        }
+
+        mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
+    }
+}
+
 struct lt_SwLineEntry
 {
     bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
@@ -2422,6 +2463,12 @@ void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) cons
             svx::frame::Style aStyles[ 7 ];
             aStyles[ 0 ] = rEntryStyle;
             FindStylesForLine( aStart, aEnd, aStyles, bHori );
+
+            if (!bHori && rEntry.mnLimitedEndPos)
+            {
+                aEnd.setY(rEntry.mnLimitedEndPos);
+            }
+
             SwRect aRepaintRect( aStart, aEnd );
 
             // the repaint rectangle has to be moved a bit for the centered lines:
@@ -2741,8 +2788,17 @@ void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem
 
     SwLineEntry aLeft  (nLeft,   nTop,  nBottom,
             bVert ? aB                         : (bR2L ? aR : aL));
+    if (bWordTableCell && rBoxItem.GetLeft())
+    {
+        aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
+    }
+
     SwLineEntry aRight (nRight,  nTop,  nBottom,
             bVert ? (bBottomAsTop ? aB : aT) : (bR2L ? aL : aR));
+    if (bWordTableCell && rBoxItem.GetRight())
+    {
+        aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
+    }
     SwLineEntry aTop   (nTop,    nLeft, nRight,
             bVert ? aL                         : (bBottomAsTop ? aB : aT));
     SwLineEntry aBottom(nBottom, nLeft, nRight,
@@ -2773,6 +2829,14 @@ void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
     while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
     {
         const SwLineEntry& rOld = *aIter;
+
+        if (rOld.mnLimitedEndPos)
+        {
+            // Don't merge with this line entry as it ends sooner than mnEndPos.
+            ++aIter;
+            continue;
+        }
+
         const SwLineEntry::OverlapType nOverlapType = rOld.Overlaps( rNew );
 
         const svx::frame::Style& rOldAttr = rOld.maAttribute;
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index a379e71dbaed..eeb2ab36658b 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -5296,6 +5296,90 @@ long SwCellFrame::GetLayoutRowSpan() const
     return  nRet;
 }
 
+const SwCellFrame* SwCellFrame::GetCoveredCellInRow(const SwRowFrame& rRow) const
+{
+    if (GetLayoutRowSpan() <= 1)
+    {
+        // Not merged vertically.
+        return nullptr;
+    }
+
+    for (const SwFrame* pCell = rRow.GetLower(); pCell; pCell = pCell->GetNext())
+    {
+        if (!pCell->IsCellFrame())
+        {
+            continue;
+        }
+
+        auto pCellFrame = static_cast<const SwCellFrame*>(pCell);
+        if (!pCellFrame->IsCoveredCell())
+        {
+            continue;
+        }
+
+        if (pCellFrame->getFrameArea().Left() != getFrameArea().Left())
+        {
+            continue;
+        }
+
+        if (pCellFrame->getFrameArea().Width() != getFrameArea().Width())
+        {
+            continue;
+        }
+
+        // pCellFrame is covered, there are only covered cell frames between "this" and pCellFrame
+        // and the horizontal position/size matches "this".
+        return pCellFrame;
+    }
+
+    return nullptr;
+}
+
+std::vector<const SwCellFrame*> SwCellFrame::GetCoveredCells() const
+{
+    std::vector<const SwCellFrame*> aRet;
+    if (GetLayoutRowSpan() <= 1)
+    {
+        return aRet;
+    }
+
+    if (!GetUpper()->IsRowFrame())
+    {
+        return aRet;
+    }
+
+    auto pFirstRowFrame = static_cast<const SwRowFrame*>(GetUpper());
+    if (!pFirstRowFrame->GetNext())
+    {
+        return aRet;
+    }
+
+    if (!pFirstRowFrame->GetNext()->IsRowFrame())
+    {
+        return aRet;
+    }
+
+    for (const SwFrame* pRow = pFirstRowFrame->GetNext(); pRow; pRow = pRow->GetNext())
+    {
+        if (!pRow->IsRowFrame())
+        {
+            continue;
+        }
+
+        auto pRowFrame = static_cast<const SwRowFrame*>(pRow);
+        const SwCellFrame* pCovered = GetCoveredCellInRow(*pRowFrame);
+        if (!pCovered)
+        {
+            continue;
+        }
+
+        // Found a cell in a next row that is covered by "this".
+        aRet.push_back(pCovered);
+    }
+
+    return aRet;
+}
+
 void SwCellFrame::dumpAsXmlAttributes(xmlTextWriterPtr pWriter) const
 {
     SwFrame::dumpAsXmlAttributes(pWriter);
commit 315508bf28a4b58b431ffa5899e9ae23cbab9eca
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Wed Feb 19 18:03:59 2020 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 13:25:02 2021 +0100

    sw table cell borders: add optional Word-compatible border collapsing
    
    We always compared border width and other aspects only after that, Word
    works with border weight according to their implementer notes.
    
    So extend svx::frame::Style to be able to collapse borders using weights
    and opt in for that from sw/ in case a compat mode (which is related to
    tables, off by default and is set by the DOC/DOCX import) is set.
    
    (cherry picked from commit e6fa52c2c371c7adc9c2c2cb18c3a8cf782cfa4b)
    
    Conflicts:
            sw/qa/core/layout/layout.cxx
    
    Change-Id: I1f682789302c88a0d326c6c0263ad3007441cb24

diff --git a/include/svx/framelink.hxx b/include/svx/framelink.hxx
index fa629fc24ad2..cab0b59e371d 100644
--- a/include/svx/framelink.hxx
+++ b/include/svx/framelink.hxx
@@ -123,6 +123,7 @@ private:
         double              mfSecn;     /// Width of secondary (right or bottom) line.
         double              mfPatternScale; /// Scale used for line pattern spacing.
         SvxBorderLineStyle  mnType;
+        bool mbWordTableCell;
 
     public:
         /** Constructs an invisible frame style. */
@@ -136,7 +137,8 @@ private:
             mfDist(0.0),
             mfSecn(0.0),
             mfPatternScale(1.0),
-            mnType(SvxBorderLineStyle::SOLID)
+            mnType(SvxBorderLineStyle::SOLID),
+            mbWordTableCell(false)
         {}
     };
 
@@ -194,6 +196,9 @@ public:
     /** Mirrors this style (exchanges primary and secondary), if it is a double frame style. */
     Style& MirrorSelf();
 
+    /** Enables the Word-compatible Style comparison code. */
+    void SetWordTableCell(bool bWordTableCell);
+
     bool operator==( const Style& rOther) const;
     bool operator<( const Style& rOther) const;
 };
diff --git a/svx/source/dialog/framelink.cxx b/svx/source/dialog/framelink.cxx
index b4548c6ebed9..c8696b063e0b 100644
--- a/svx/source/dialog/framelink.cxx
+++ b/svx/source/dialog/framelink.cxx
@@ -267,6 +267,16 @@ Style& Style::MirrorSelf()
     return *this;
 }
 
+void Style::SetWordTableCell(bool bWordTableCell)
+{
+    if (!maImplStyle)
+    {
+        implEnsureImplStyle();
+    }
+
+    maImplStyle->mbWordTableCell = bWordTableCell;
+}
+
 bool Style::operator==( const Style& rOther) const
 {
     if(!maImplStyle && !rOther.maImplStyle)
@@ -290,6 +300,101 @@ bool Style::operator==( const Style& rOther) const
         && Type() == rOther.Type());
 }
 
+namespace
+{
+/**
+ * Gets the weight of rStyle, according to [MS-OI29500] v20171130, 2.1.168 Part 1 Section 17.4.66,
+ * tcBorders (Table Cell Borders).
+ */
+double GetWordTableCellBorderWeight(const Style& rStyle)
+{
+    double fWidth = rStyle.GetWidth();
+    int nBorderNumber = 0;
+
+    // See lcl_convertBorderStyleFromToken() in writerfilter/ and ConvertBorderStyleFromWord() in
+    // editeng/, this is the opposite of the combination of those functions.
+    switch (rStyle.Type())
+    {
+        case SvxBorderLineStyle::NONE:
+            return 0.0;
+        case SvxBorderLineStyle::DOTTED:
+        case SvxBorderLineStyle::DASHED:
+            return 1.0;
+        case SvxBorderLineStyle::SOLID:
+            // single = 1
+            // thick = 2
+            // wave = 20
+            nBorderNumber = 1;
+            break;
+        case SvxBorderLineStyle::DOUBLE:
+        case SvxBorderLineStyle::DOUBLE_THIN:
+            // double = 3
+            // triple = 10
+            // doubleWave = 21
+            // dashDotStroked = 23
+            nBorderNumber = 3;
+            break;
+        case SvxBorderLineStyle::DASH_DOT:
+            // dotDash = 8
+            nBorderNumber = 8;
+            break;
+        case SvxBorderLineStyle::DASH_DOT_DOT:
+            // dotDotDash = 9
+            nBorderNumber = 9;
+            break;
+        case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+            // thinThickSmallGap = 11
+            nBorderNumber = 11;
+            break;
+        case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+            // thickThinSmallGap = 12
+            // thinThickThinSmallGap = 13
+            nBorderNumber = 12;
+            break;
+        case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+            // thinThickMediumGap = 14
+            nBorderNumber = 14;
+            break;
+        case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+            // thickThinMediumGap = 15
+            // thinThickThinMediumGap = 16
+            nBorderNumber = 15;
+            break;
+        case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+            // thinThickLargeGap = 17
+            nBorderNumber = 17;
+            break;
+        case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+            // thickThinLargeGap = 18
+            // thinThickThinLargeGap = 19
+            nBorderNumber = 18;
+            break;
+        case SvxBorderLineStyle::FINE_DASHED:
+            // dashSmallGap = 22
+            nBorderNumber = 22;
+            break;
+        case SvxBorderLineStyle::EMBOSSED:
+            // threeDEmboss = 24
+            nBorderNumber = 24;
+            break;
+        case SvxBorderLineStyle::ENGRAVED:
+            // threeDEngrave = 25
+            nBorderNumber = 25;
+            break;
+        case SvxBorderLineStyle::OUTSET:
+            // outset = 26
+            nBorderNumber = 25;
+            break;
+        case SvxBorderLineStyle::INSET:
+            // inset = 27
+            nBorderNumber = 27;
+            break;
+    }
+
+    return nBorderNumber * fWidth;
+}
+}
+
 bool Style::operator<( const Style& rOther) const
 {
     if(!maImplStyle && !rOther.maImplStyle)
@@ -298,6 +403,18 @@ bool Style::operator<( const Style& rOther) const
         return false;
     }
 
+    if (maImplStyle && maImplStyle->mbWordTableCell)
+    {
+        // The below code would first compare based on the border width, Word compares based on its
+        // calculated weight, do that in the compat case.
+        double fLW = GetWordTableCellBorderWeight(*this);
+        double fRW = GetWordTableCellBorderWeight(rOther);
+        if (!rtl::math::approxEqual(fLW, fRW))
+        {
+            return fLW < fRW;
+        }
+    }
+
     // different total widths -> this<rOther, if this is thinner
     double nLW = GetWidth();
     double nRW = rOther.GetWidth();
diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx
index de14fa4348ae..fc15487b774c 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -2708,11 +2708,23 @@ void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem
     bool const bVert = mrTabFrame.IsVertical();
     bool const bR2L  = mrTabFrame.IsRightToLeft();
 
+    bool bWordTableCell = false;
+    SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
+    if (pShell)
+    {
+        const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
+        bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+    }
+
     // no scaling needed, it's all in the primitives and the target device
     svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
+    aL.SetWordTableCell(bWordTableCell);
     svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
+    aR.SetWordTableCell(bWordTableCell);
     svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
+    aT.SetWordTableCell(bWordTableCell);
     svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
+    aB.SetWordTableCell(bWordTableCell);
 
     aR.MirrorSelf();
     aB.MirrorSelf();
commit 8f3305936c8c852f36cb7abb2f29f9b6631c032b
Author:     Justin Luth <justin.luth at collabora.com>
AuthorDate: Thu Jun 4 18:46:23 2020 +0300
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 11:36:46 2021 +0100

    tdf#132898 writerfilter: use last merged cell's bottom border
    
    Previously, this was just using the border set on the first
    cell. Obviously, the bottom merged cell should contain the
    definitive setting for the bottom border.
    
    This depends on all of the commits for tdf#129452.
    
    P.S. The top border is obviously fine defined by the top merged cell.
    
    P.S. The left/right borders are a bit more interesting.
    In MSO, each row's value applies to that section of the
    merged cell. I.E., it isn't treated as a cell border at all.
    Only noted as a comment here, since how to deal with it
    is probably best left as is - defined by the top cell.
    
    (cherry picked from commit 03803de58bd426eb0b726437dc205d92383e8e2e)
    
    Conflicts:
            sw/qa/extras/ooxmlexport/ooxmlexport15.cxx
            writerfilter/source/dmapper/DomainMapperTableHandler.cxx
    
    Change-Id: Ibe8d31cba5122078ce73020f7816bff0b2ae36c6

diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index c0facb42b3dc..4cb518eb29d8 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -850,16 +850,28 @@ CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(Tabl
                 if ( bMergedVertically )
                 {
                     const sal_uInt32 nColumn = m_rDMapper_Impl.getTableManager().findColumn(nRow, nCell);
+                    sal_Int32 nLastMergedRow = 0;
                     for (size_t i = nRow + 1; bMergedVertically && i < m_aCellProperties.size(); i++)
                     {
                         const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(i, nColumn);
                         if ( m_aCellProperties[i].size() > sal::static_int_cast<std::size_t>(nColumnCell) )
                         {
                             bMergedVertically = bool(m_aCellProperties[i][nColumnCell]->getProperty(PROP_VERTICAL_MERGE));
+                            if ( bMergedVertically )
+                                nLastMergedRow = i;
                         }
                         else
                             bMergedVertically = false;
                     }
+
+                    // Only consider the bottom border setting from the last merged cell.
+                    // Note: in MSO, left/right apply per-unmerged-row. Can't do that in LO, so just using the top cell's borders should be fine.
+                    if ( nRow < nLastMergedRow )
+                    {
+                        (*aCellIterator)->Erase(PROP_BOTTOM_BORDER);
+                        const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(nLastMergedRow, nColumn);
+                        lcl_mergeBorder( PROP_BOTTOM_BORDER, m_aCellProperties[nLastMergedRow][nColumnCell], *aCellIterator );
+                    }
                 }
 
                 lcl_computeCellBorders( rInfo.pTableBorders, *aCellIterator, nCell, nRow, bIsEndCol, bIsEndRow, bMergedVertically );
commit 323d1b7eb2137df1dd95882a5c37799b8936dde8
Author:     Justin Luth <justin.luth at collabora.com>
AuthorDate: Mon Jun 29 16:47:24 2020 +0300
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 11:31:58 2021 +0100

    tdf#129452 writerfilter: use column, not cell when comparing rows
    
    A natural mistake - especially when there are no existing
    functions - is to assume that cell number defines column
    number, and so it is valid to compare between rows.
    
    This patch provides the functions, and switches this bug's
    earlier patch to properly use columns instead of cells.
    
    This commit depends on two prior patches in bug 129452:
      Change-Id: Ie305477f0e3468a4a923095d76f520d97fe99ffe
      Change-Id: Ibfdac336bbb1f7303c7e585a85c94be37ad6f916
    
    I hope that this implementation covers all the bases.
    This code is dreadful to understand,
    as witnessed by comments from those much smarter than I.
    
    P.S. I also cancelled a vertical merge if the cell
    is not there (in a gridAfter situation).
    If a row is shorter than the previous ones,
    then a vertically merged cell should not be considered
    to be able to span that gap. If the cells below are still
    merged, it would be considered a new merged cell.
    
    (cherry picked from commit 19d7f9624e92422409ed2744091d502fdae8692b)
    
    Conflicts:
            sw/qa/extras/ooxmlexport/ooxmlexport15.cxx
            writerfilter/source/dmapper/DomainMapperTableHandler.cxx
            writerfilter/source/dmapper/TableManager.cxx
    
    Change-Id: I63158ac73b1bf86d9f75dd3c1299d1b1a3f08064

diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index 1391e47127bd..c0facb42b3dc 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -709,6 +709,9 @@ CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(Tabl
             }
         }
 
+        // Note that this is intentionally called "cell" and not "column".
+        // Don't make the mistake that all cell x's will be in the same column.
+        // Merged cells (grid span) in a row will affect the actual column. (fake cells were added to handle gridBefore)
         sal_Int32 nCell = 0;
         pCellProperties[nRow].realloc( aRowOfCellsIterator->size() );
         beans::PropertyValues* pSingleCellProperties = pCellProperties[nRow].getArray();
@@ -844,10 +847,20 @@ CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(Tabl
                 // tdf#129452 Checking if current cell is vertically merged with all the other cells below to the bottom.
                 // This must be done in order to apply the bottom border of the table to the first cell in a vertical merge.
                 bool bMergedVertically = bool(m_aCellProperties[nRow][nCell]->getProperty(PROP_VERTICAL_MERGE));
-
-                for (size_t i = nRow + 1; bMergedVertically && i < m_aCellProperties.size(); i++)
-                    if ( m_aCellProperties[i].size() > sal::static_int_cast<std::size_t>(nCell) )
-                        bMergedVertically = bool(m_aCellProperties[i][nCell]->getProperty(PROP_VERTICAL_MERGE));
+                if ( bMergedVertically )
+                {
+                    const sal_uInt32 nColumn = m_rDMapper_Impl.getTableManager().findColumn(nRow, nCell);
+                    for (size_t i = nRow + 1; bMergedVertically && i < m_aCellProperties.size(); i++)
+                    {
+                        const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(i, nColumn);
+                        if ( m_aCellProperties[i].size() > sal::static_int_cast<std::size_t>(nColumnCell) )
+                        {
+                            bMergedVertically = bool(m_aCellProperties[i][nColumnCell]->getProperty(PROP_VERTICAL_MERGE));
+                        }
+                        else
+                            bMergedVertically = false;
+                    }
+                }
 
                 lcl_computeCellBorders( rInfo.pTableBorders, *aCellIterator, nCell, nRow, bIsEndCol, bIsEndRow, bMergedVertically );
 
diff --git a/writerfilter/source/dmapper/TableManager.cxx b/writerfilter/source/dmapper/TableManager.cxx
index dd950c3c9d9b..ea18174ea5ce 100644
--- a/writerfilter/source/dmapper/TableManager.cxx
+++ b/writerfilter/source/dmapper/TableManager.cxx
@@ -78,6 +78,40 @@ void TableManager::setCurrentGridSpan(sal_uInt32 nGridSpan)
     mTableDataStack.top()->getCurrentRow()->setCurrentGridSpan(nGridSpan);
 }
 
+sal_uInt32 TableManager::findColumn(const sal_uInt32 nRow, const sal_uInt32 nCell)
+{
+    RowData::Pointer_t pRow = mTableDataStack.top()->getRow(nRow);
+    if (!pRow || nCell < pRow->getGridBefore() || nCell >= pRow->getCellCount())
+        return SAL_MAX_UINT32;
+
+    // The gridSpans provide a one-based index, so add up all the spans of the PREVIOUS columns,
+    // and that result will provide the first possible zero-based number for the desired column.
+    sal_uInt32 nColumn = 0;
+    for (sal_uInt32 n = 0; n < nCell; ++n)
+        nColumn += pRow->getGridSpan(n);
+    return nColumn;
+}
+
+sal_uInt32 TableManager::findColumnCell(const sal_uInt32 nRow, const sal_uInt32 nCol)
+{
+    RowData::Pointer_t pRow = mTableDataStack.top()->getRow(nRow);
+    if (!pRow || nCol < pRow->getGridBefore())
+        return SAL_MAX_UINT32;
+
+    sal_uInt32 nCell = 0;
+    sal_uInt32 nGrids = 0;
+    // The gridSpans give us a one-based index, but requested column is zero-based - so keep that in mind.
+    for (const auto& rSpan : pRow->getGridSpans())
+    {
+        nGrids += rSpan;
+        if (nCol < nGrids)
+            return nCell;
+
+        ++nCell;
+    }
+    return SAL_MAX_UINT32; // must be in gridAfter or invalid column request
+}
+
 void TableManager::endOfCellAction()
 {
 }
diff --git a/writerfilter/source/dmapper/TableManager.hxx b/writerfilter/source/dmapper/TableManager.hxx
index f57a23a57f4a..98ac4fbe32bb 100644
--- a/writerfilter/source/dmapper/TableManager.hxx
+++ b/writerfilter/source/dmapper/TableManager.hxx
@@ -469,6 +469,10 @@ public:
     void setCurrentGridBefore( sal_uInt32 nSkipGrids );
     std::vector<sal_uInt32> getCurrentGridSpans();
     void setCurrentGridSpan( sal_uInt32 nGridSpan );
+    /// Given a zero-based row/cell, return the zero-based grid it belongs to, or SAL_MAX_UINT16 for invalid.
+    sal_uInt32 findColumn( const sal_uInt32 nRow, const sal_uInt32 nCell );
+    /// Given a zero-based row/col, return the zero-based cell describing that grid, or SAL_MAX_UINT16 for invalid.
+    sal_uInt32 findColumnCell( const sal_uInt32 nRow, const sal_uInt32 nCol );
 
     void setTableStartsAtCellStart(bool bTableStartsAtCellStart);
     void setCellLastParaAfterAutospacing(bool bIsAfterAutospacing);
commit b743f95b8d3b406da3d9b8c9ad017411f1351ad2
Author:     László Németh <nemeth at numbertext.org>
AuthorDate: Tue Feb 4 19:31:41 2020 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 11:04:35 2021 +0100

    tdf#129575 DOCX import: fix table style preference
    
    handling by recognizing docDefault properties
    instead of default-value based heuristics.
    
    (cherry picked from commit f15d67442972c5f69c71925a6bfa5aa1a39d54eb)
    
    Conflicts:
            sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
            sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
            writerfilter/source/dmapper/PropertyIds.cxx
            writerfilter/source/dmapper/PropertyMap.cxx
            writerfilter/source/dmapper/PropertyMap.hxx
            writerfilter/source/dmapper/StyleSheetTable.cxx
            writerfilter/source/dmapper/StyleSheetTable.hxx
    
    Change-Id: I3bab9d85d77d0e5f1c357121b1caf02cbe4899c4

diff --git a/sw/qa/extras/ooxmlexport/data/tdf129575-directAfter.docx b/sw/qa/extras/ooxmlexport/data/tdf129575-directAfter.docx
new file mode 100644
index 000000000000..c9856b02857b
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf129575-directAfter.docx differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf129575-directBefore.docx b/sw/qa/extras/ooxmlexport/data/tdf129575-directBefore.docx
new file mode 100644
index 000000000000..5e75ef1be5d2
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf129575-directBefore.docx differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf129575-docDefault.docx b/sw/qa/extras/ooxmlexport/data/tdf129575-docDefault.docx
new file mode 100644
index 000000000000..d7cdf2ec4308
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf129575-docDefault.docx differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf129575-styleAfter.docx b/sw/qa/extras/ooxmlexport/data/tdf129575-styleAfter.docx
new file mode 100644
index 000000000000..97439011ff55
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf129575-styleAfter.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
index cd1665489db5..4eae1b2a33df 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
@@ -465,9 +465,9 @@ DECLARE_OOXMLEXPORT_TEST(testTdf119054, "tdf119054.docx")
     xmlDocPtr pXmlDoc = parseExport();
     if (!pXmlDoc)
         return;
-    // Don't overwrite before and after spacing of Heading2 by table style
-    assertXPathNoAttribute(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "before");
-    assertXPathNoAttribute(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "after");
+    // Overwrite applied table style with before and after spacing of Heading2
+    assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "before", "0");
+    assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "after", "360");
     // Use table style based single line spacing instead of the docDefaults' 254
     assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "line", "240");
 }
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx
index e396ac0bea0b..9d6642a153ec 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx
@@ -31,6 +31,7 @@
 #include <com/sun/star/view/XSelectionSupplier.hpp>
 #include <com/sun/star/style/LineSpacing.hpp>
 #include <com/sun/star/style/LineSpacingMode.hpp>
+//#include <com/sun/star/drawing/LineStyle.hpp>
 #include <com/sun/star/style/ParagraphAdjust.hpp>
 #include <com/sun/star/drawing/XControlShape.hpp>
 #include <com/sun/star/text/TextContentAnchorType.hpp>
@@ -159,6 +160,70 @@ DECLARE_OOXMLEXPORT_TEST(testTdf106690Cell, "tdf106690-cell.docx")
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(494), getProperty<sal_Int32>(getParagraphOfText(2, xCell->getText()), "ParaBottomMargin"));
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf129575_directBefore, "tdf129575-directBefore.docx")
+{
+    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY);
+    uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
+    uno::Reference<text::XTextRange> xCell(xTable->getCellByName("A1"), uno::UNO_QUERY);
+    // direct paragraph formatting
+    // This was 212 twips from the table style, but always direct paragraph formatting wins, in the case of the default 0 margin, too
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), getProperty<sal_Int32>(getParagraphOfText(1, xCell->getText()), "ParaTopMargin"));
+    // default margin
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), getProperty<sal_Int32>(getParagraphOfText(1, xCell->getText()), "ParaBottomMargin"));
+}
+
+DECLARE_OOXMLEXPORT_TEST(testTdf129575_directAfter, "tdf129575-directAfter.docx")
+{
+    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY);
+    uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
+    uno::Reference<text::XTextRange> xCell(xTable->getCellByName("A1"), uno::UNO_QUERY);
+    // from table style
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(212), getProperty<sal_Int32>(getParagraphOfText(1, xCell->getText()), "ParaTopMargin"));
+    // direct paragraph formatting
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), getProperty<sal_Int32>(getParagraphOfText(1, xCell->getText()), "ParaBottomMargin"));
+}
+
+DECLARE_OOXMLEXPORT_TEST(testTdf129575_styleAfter, "tdf129575-styleAfter.docx")
+{
+    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY);
+    uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
+    uno::Reference<text::XTextRange> xCell(xTable->getCellByName("A1"), uno::UNO_QUERY);
+    // direct paragraph formatting
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), getProperty<sal_Int32>(getParagraphOfText(1, xCell->getText()), "ParaTopMargin"));
+    // from table style
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(212), getProperty<sal_Int32>(getParagraphOfText(1, xCell->getText()), "ParaBottomMargin"));
+}
+
+DECLARE_OOXMLEXPORT_TEST(testTdf129575_docDefault, "tdf129575-docDefault.docx")
+{
+    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY);
+    uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
+    uno::Reference<text::XTextRange> xCell(xTable->getCellByName("A1"), uno::UNO_QUERY);
+    // docDefault defines both bottom margin and line spacing, but
+    // applied bottom margin values are based on non-docDefault paragraph styles, line spacing is based on table style
+
+    // docDefault: <w:spacing w:after="160" w:line="320" w:lineRule="auto"/>
+    // table style: <w:spacing w:after="0" w:line="240" w:lineRule="auto"/> (single line space, overwriting bigger docDefault)
+
+    // Paragraph style Normal: <w:spacing w:after="160"/> (same as docDefault),
+    // table style based single line spacing
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(282), getProperty<sal_Int32>(getParagraphOfText(1, xCell->getText()), "ParaBottomMargin"));
+    style::LineSpacing aLineSpacing = getProperty<style::LineSpacing>(getParagraphOfText(1, xCell->getText()), "ParaLineSpacing");
+    CPPUNIT_ASSERT_EQUAL(sal_Int16(style::LineSpacingMode::PROP), aLineSpacing.Mode);
+    CPPUNIT_ASSERT_EQUAL(sal_Int16(100), aLineSpacing.Height);
+    // Heading 2: <w:spacing w:after="360"/> (different from docDefault),
+    // table style based single line spacing
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(635), getProperty<sal_Int32>(getParagraphOfText(2, xCell->getText()), "ParaBottomMargin"));
+    aLineSpacing = getProperty<style::LineSpacing>(getParagraphOfText(1, xCell->getText()), "ParaLineSpacing");
+    CPPUNIT_ASSERT_EQUAL(sal_Int16(style::LineSpacingMode::PROP), aLineSpacing.Mode);
+    CPPUNIT_ASSERT_EQUAL(sal_Int16(100), aLineSpacing.Height);
+
+}
+
 DECLARE_OOXMLEXPORT_TEST(testTdf106970, "tdf106970.docx")
 {
     // The second paragraph (first numbered one) had 0 bottom margin:
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index a0b077282885..1391e47127bd 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -61,6 +61,7 @@ using namespace ::std;
 #define CNF_FIRST_ROW_FIRST_COLUMN  0x004
 #define CNF_LAST_ROW_LAST_COLUMN    0x002
 #define CNF_LAST_ROW_FIRST_COLUMN   0x001
+#define CNF_ALL                     0xFFF
 
 DomainMapperTableHandler::DomainMapperTableHandler(
             css::uno::Reference<css::text::XTextAppendAndConvert> const& xText,
@@ -226,6 +227,7 @@ struct TableInfo
     PropertyMapPtr pTableBorders;
     TableStyleSheetEntry* pTableStyle;
     css::beans::PropertyValues aTableProperties;
+    std::vector< PropertyIds > aTablePropertyIds;
 
     TableInfo()
     : nLeftBorderDistance(DEF_BORDER_DIST)
@@ -636,6 +638,7 @@ TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo
         }
 
         rInfo.aTableProperties = m_aTableProperties->GetPropertyValues();
+        rInfo.aTablePropertyIds = m_aTableProperties->GetPropertyIds();
 
 #ifdef DEBUG_WRITERFILTER
         TagLogger::getInstance().startElement("debug.tableprops");
@@ -810,9 +813,6 @@ CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(Tabl
                 // Remove properties from style/row that aren't allowed in cells
                 pAllCellProps->Erase( PROP_HEADER_ROW_COUNT );
                 pAllCellProps->Erase( PROP_TBL_HEADER );
-                // Remove paragraph properties from style/row that paragraph style can overwrite
-                pAllCellProps->Erase( PROP_PARA_BOTTOM_MARGIN );
-                pAllCellProps->Erase( PROP_PARA_LINE_SPACING );
 
                 // Then add the cell properties
                 pAllCellProps->InsertProps(*aCellIterator);
@@ -1018,29 +1018,59 @@ css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTabl
 
 // table style has got bigger precedence than docDefault style,
 // but lower precedence than the paragraph styles and direct paragraph formatting
-void DomainMapperTableHandler::ApplyParaProperty(css::beans::PropertyValues aTableProperties, PropertyIds eId)
+void DomainMapperTableHandler::ApplyParagraphPropertiesFromTableStyle(TableInfo & rInfo)
 {
-    OUString sPropertyName = getPropertyName(eId);
-    auto pTableProp = std::find_if(aTableProperties.begin(), aTableProperties.end(),
-        [&](const beans::PropertyValue& rProp) { return rProp.Name == sPropertyName; });
-    if (pTableProp != aTableProperties.end())
+    for( auto const& eId : rInfo.aTablePropertyIds )
     {
-        uno::Any aValue = pTableProp->Value;
-        for (const auto& rParaProp : m_rDMapper_Impl.m_aParagraphsToEndTable)
+        // apply paragraph and character properties of the table style on table paragraphs
+        if ( isParagraphProperty(eId) || isCharacterProperty(eId) )
         {
-            // there is no direct paragraph formatting
-            if (!rParaProp.m_pPropertyMap->isSet(eId))
+            // check all paragraphs of the table
+            for (const auto& rParaProp : m_rDMapper_Impl.m_aParagraphsToEndTable)
             {
-                OUString sParaStyleName;
-                rParaProp.m_rPropertySet->getPropertyValue("ParaStyleName") >>= sParaStyleName;
-                StyleSheetEntryPtr pEntry = m_rDMapper_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sParaStyleName);
-                uno::Any aMargin = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, pEntry, true, true);
-                uno::Any aMarginDocDefault = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, nullptr, true, true);
-                // use table style only when 1) both values are empty (no docDefault and paragraph style definitions) or
-                // 2) both non-empty values are equal (docDefault paragraph properties are copied to the base paragraph style during import)
-                // TODO check the case, when two parent styles modify the docDefault and the last one set back the docDefault value
-                if (aMargin == aMarginDocDefault)
-                    rParaProp.m_rPropertySet->setPropertyValue(sPropertyName, aValue);
+                // there is no direct paragraph formatting
+                if (!rParaProp.m_pPropertyMap->isSet(eId))
+                {
+                    bool bDocDefault;
+                    OUString sParaStyleName;
+                    rParaProp.m_rPropertySet->getPropertyValue("ParaStyleName") >>= sParaStyleName;
+                    StyleSheetEntryPtr pEntry = m_rDMapper_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sParaStyleName);
+                    uno::Any aParaStyle = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, pEntry, true, true, &bDocDefault);
+                    // use table style when a docDefault value is applied instead of it,
+                    // and there is no associated TableStyleSheetEntry
+                    // TODO: replace CNF_ALL with the actual mask
+                    if ( (aParaStyle == uno::Any() || bDocDefault) && !rInfo.pTableStyle->GetProperties(CNF_ALL)->getProperty(eId) )
+                    {
+                        OUString sPropertyName = getPropertyName(eId);
+                        auto pTableProp = std::find_if(rInfo.aTableProperties.begin(), rInfo.aTableProperties.end(),
+                            [&](const beans::PropertyValue& rProp) { return rProp.Name == sPropertyName; });
+                        if (pTableProp != rInfo.aTableProperties.end())
+                        {
+                            try
+                            {
+                                rParaProp.m_rPropertySet->setPropertyValue( sPropertyName, pTableProp->Value );
+                            }
+                            catch ( const uno::Exception & )
+                            {
+                                SAL_WARN("writerfilter.dmapper", "Exception during table style correction");
+                            }
+                        }
+                    }
+                    // table style can overwrite paragraph style, when the paragraph style property has a default value, restore it
+                    // TODO remove the associated TableStyleSheetEntry styles, if needed
+                    else if ( aParaStyle != uno::Any() && !bDocDefault )
+                    {
+                        OUString sPropertyName = getPropertyName(eId);
+                        try
+                        {
+                            rParaProp.m_rPropertySet->setPropertyValue( sPropertyName, aParaStyle );
+                        }
+                        catch ( const uno::Exception & )
+                        {
+                            SAL_WARN("writerfilter.dmapper", "Exception during table style correction");
+                        }
+                    }
+                }
             }
         }
     }
@@ -1144,9 +1174,8 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
                     }
                 }
 
-                // OOXML table style may container paragraph properties, apply these now.
-                ApplyParaProperty(aTableInfo.aTableProperties, PROP_PARA_BOTTOM_MARGIN);
-                ApplyParaProperty(aTableInfo.aTableProperties, PROP_PARA_LINE_SPACING);
+                // OOXML table style may contain paragraph properties, apply these now.
+                ApplyParagraphPropertiesFromTableStyle(aTableInfo);
             }
         }
         catch ( const lang::IllegalArgumentException &e )
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
index 16d2a0cc37cc..b454be94f563 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
@@ -91,7 +91,7 @@ public:
      */
     void startTable(const TablePropertyMapPtr& pProps);
 
-    void ApplyParaProperty(css::beans::PropertyValues aTableProperties, PropertyIds eId);
+    void ApplyParagraphPropertiesFromTableStyle(TableInfo & rInfo);
 
     /// Handle end of table.
     void endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart);
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 2489f50ba94e..716c14e8a5bf 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -704,7 +704,7 @@ const OUString DomainMapper_Impl::GetDefaultParaStyleName()
     return m_sDefaultParaStyleName;
 }
 
-uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara)
+uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, bool* pIsDocDefault)
 {
     while(pEntry.get( ) )
     {
@@ -714,6 +714,9 @@ uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleShee
                     pEntry->pProperties->getProperty(eId);
             if( aProperty )
             {
+                if (pIsDocDefault)
+                    *pIsDocDefault = pEntry->pProperties->isDocDefault(eId);
+
                 return aProperty->second;
             }
         }
@@ -737,7 +740,12 @@ uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleShee
         {
             boost::optional<PropertyMap::Property> aProperty = pDefaultParaProps->getProperty(eId);
             if ( aProperty )
+            {
+                if (pIsDocDefault)
+                    *pIsDocDefault = true;
+
                 return aProperty->second;
+            }
         }
     }
     if ( bDocDefaults && isCharacterProperty(eId) )
@@ -747,9 +755,18 @@ uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleShee
         {
             boost::optional<PropertyMap::Property> aProperty = pDefaultCharProps->getProperty(eId);
             if ( aProperty )
+            {
+                if (pIsDocDefault)
+                    *pIsDocDefault = true;
+
                 return aProperty->second;
+            }
         }
     }
+
+    if (pIsDocDefault)
+        *pIsDocDefault = false;
+
     return uno::Any();
 }
 
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index cf681ea34deb..955d806e5bab 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -716,7 +716,7 @@ public:
     const OUString  GetDefaultParaStyleName();
 
     // specified style - including inherited properties. Indicate whether paragraph defaults should be checked.
-    css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara);
+    css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, bool* bIsDocDefault = nullptr);
     // current paragraph style - including inherited properties
     css::uno::Any GetPropertyFromParaStyleSheet(PropertyIds eId);
     // context's character style - including inherited properties
diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx
index 562f5f1795b1..35638f774c79 100644
--- a/writerfilter/source/dmapper/PropertyIds.cxx
+++ b/writerfilter/source/dmapper/PropertyIds.cxx
@@ -366,6 +366,11 @@ bool isCharacterProperty( const PropertyIds eId )
     return eId > PROP_CHARACTER_STYLES && eId < PROP_CHARACTER_END;
 }
 
+bool isParagraphProperty( const PropertyIds eId )
+{
+    return eId >= PROP_PARA_ADJUST && eId <= PROP_PARA_WIDOWS;
+}
+
 } //namespace dmapper
 } //namespace writerfilter
 
diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx
index 6b3136fe10a5..392c25e68b5a 100644
--- a/writerfilter/source/dmapper/PropertyIds.hxx
+++ b/writerfilter/source/dmapper/PropertyIds.hxx
@@ -363,6 +363,8 @@ OUString getPropertyName(PropertyIds eId);
 
 bool isCharacterProperty(const PropertyIds eId);
 
+bool isParagraphProperty(const PropertyIds eId);
+
 } //namespace dmapper
 } // namespace writerfilter
 #endif
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
index 5d51a89e976a..f1448dbd4423 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -189,6 +189,14 @@ uno::Sequence< beans::PropertyValue > PropertyMap::GetPropertyValues( bool bChar
     return comphelper::containerToSequence( m_aValues );
 }
 
+std::vector< PropertyIds > PropertyMap::GetPropertyIds()
+{
+    std::vector< PropertyIds > aRet;
+    for ( const auto& rPropPair : m_vMap )
+        aRet.push_back( rPropPair.first );
+    return aRet;
+}
+
 #ifdef DEBUG_WRITERFILTER
 static void lcl_AnyToTag( const uno::Any& rAny )
 {
@@ -227,7 +235,7 @@ static void lcl_AnyToTag( const uno::Any& rAny )
 }
 #endif
 
-void PropertyMap::Insert( PropertyIds eId, const uno::Any& rAny, bool bOverwrite, GrabBagType i_GrabBagType )
+void PropertyMap::Insert( PropertyIds eId, const uno::Any& rAny, bool bOverwrite, GrabBagType i_GrabBagType, bool bDocDefault )
 {
 #ifdef DEBUG_WRITERFILTER
     const OUString& rInsert = getPropertyName(eId);
@@ -239,7 +247,7 @@ void PropertyMap::Insert( PropertyIds eId, const uno::Any& rAny, bool bOverwrite
 #endif
 
     if ( !bOverwrite )
-        m_vMap.insert(std::make_pair(eId, PropValue(rAny, i_GrabBagType)));
+        m_vMap.insert(std::make_pair(eId, PropValue(rAny, i_GrabBagType, bDocDefault)));
     else
         m_vMap[eId] = PropValue(rAny, i_GrabBagType);
 
@@ -268,6 +276,15 @@ bool PropertyMap::isSet( PropertyIds eId) const
     return m_vMap.find( eId ) != m_vMap.end();
 }
 
+bool PropertyMap::isDocDefault( PropertyIds eId ) const
+{
+    std::map< PropertyIds, PropValue >::const_iterator aIter = m_vMap.find( eId );
+    if ( aIter == m_vMap.end() )
+        return false;
+    else
+        return aIter->second.getIsDocDefault();
+}
+
 #ifdef DEBUG_WRITERFILTER
 void PropertyMap::dumpXml() const
 {
@@ -324,7 +341,12 @@ void PropertyMap::InsertProps( const PropertyMapPtr& rMap, const bool bOverwrite
         for ( const auto& rPropPair : rMap->m_vMap )
         {
             if ( bOverwrite || !m_vMap.count(rPropPair.first) )
-                m_vMap[rPropPair.first] = rPropPair.second;
+            {
+                if ( !bOverwrite && !rPropPair.second.getIsDocDefault() )
+                    m_vMap.insert(std::make_pair(rPropPair.first, PropValue(rPropPair.second.getValue(), rPropPair.second.getGrabBagType(), true)));
+                else
+                    m_vMap[rPropPair.first] = rPropPair.second;
+            }
         }
 
         insertTableProperties( rMap.get(), bOverwrite );
diff --git a/writerfilter/source/dmapper/PropertyMap.hxx b/writerfilter/source/dmapper/PropertyMap.hxx
index 6f16de26fe0a..9289534cc445 100644
--- a/writerfilter/source/dmapper/PropertyMap.hxx
+++ b/writerfilter/source/dmapper/PropertyMap.hxx
@@ -95,23 +95,35 @@ class PropValue
 private:
     css::uno::Any m_aValue;
     GrabBagType   m_GrabBagType;
+    bool          m_bIsDocDefault;
 
 public:
+    PropValue( const css::uno::Any& rValue, GrabBagType i_GrabBagType, bool bDocDefault )
+        : m_aValue( rValue )
+        , m_GrabBagType( i_GrabBagType )
+        , m_bIsDocDefault( bDocDefault )
+    {
+    }
+
     PropValue( const css::uno::Any& rValue, GrabBagType i_GrabBagType )
         : m_aValue( rValue )
         , m_GrabBagType( i_GrabBagType )
+        , m_bIsDocDefault( false )
     {
     }
 
     PropValue()
         : m_aValue()
         , m_GrabBagType( NO_GRAB_BAG )
+        , m_bIsDocDefault( false )
     {
     }
 
     const css::uno::Any& getValue() const { return m_aValue; }
 
     GrabBagType getGrabBagType() const { return m_GrabBagType; }
+
+    bool getIsDocDefault() const { return m_bIsDocDefault; }
 };
 
 class PropertyMap : public virtual SvRefBase
@@ -135,13 +147,15 @@ public:
     // the contained properties are their Value.
     css::uno::Sequence< css::beans::PropertyValue > GetPropertyValues( bool bCharGrabBag = true );
 
+    std::vector< PropertyIds > GetPropertyIds();
+
     // Add property, optionally overwriting existing attributes
-    void Insert( PropertyIds eId, const css::uno::Any& rAny, bool bOverwrite = true, GrabBagType i_GrabBagType = NO_GRAB_BAG );
+    void Insert( PropertyIds eId, const css::uno::Any& rAny, bool bOverwrite = true, GrabBagType i_GrabBagType = NO_GRAB_BAG, bool bDocDefault = false );
 
     // Remove a named property from *this, does nothing if the property id has not been set
     void Erase( PropertyIds eId);
 
-    // Imports properties from pMap
+    // Imports properties from pMap (bOverwrite==false means m_bIsDocDefault=true setting)
     void InsertProps( const tools::SvRef< PropertyMap >& rMap, const bool bOverwrite = true );
 
     // Returns a copy of the property if it exists, .first is its PropertyIds and .second is its Value (type css::uno::Any)
@@ -149,6 +163,7 @@ public:
 
     // Has the property named been set (via Insert)?
     bool isSet( PropertyIds eId ) const;
+    bool isDocDefault( PropertyIds eId ) const;
 
     const css::uno::Reference< css::text::XFootnote >& GetFootnote() const { return m_xFootnote; }
 
diff --git a/writerfilter/source/dmapper/StyleSheetTable.cxx b/writerfilter/source/dmapper/StyleSheetTable.cxx
index 08145c98ad2f..31373d988611 100644
--- a/writerfilter/source/dmapper/StyleSheetTable.cxx
+++ b/writerfilter/source/dmapper/StyleSheetTable.cxx
@@ -397,6 +397,11 @@ StyleSheetTable::~StyleSheetTable()
 {
 }
 
+void StyleSheetTable::SetDefaultParaProps(PropertyIds eId, const css::uno::Any& rAny)
+{
+    m_pImpl->m_pDefaultParaProps->Insert(eId, rAny, /*bOverwrite=*/false, NO_GRAB_BAG, /*bDocDefault=*/true);
+}
+
 PropertyMapPtr const & StyleSheetTable::GetDefaultParaProps()
 {
     return m_pImpl->m_pDefaultParaProps;
@@ -692,7 +697,7 @@ void StyleSheetTable::lcl_sprm(Sprm & rSprm)
             if ( nSprmId == NS_ooxml::LN_CT_DocDefaults_pPrDefault && m_pImpl->m_pDefaultParaProps.get() &&
                 !m_pImpl->m_pDefaultParaProps->isSet( PROP_PARA_TOP_MARGIN ) )
             {
-                m_pImpl->m_pDefaultParaProps->Insert( PROP_PARA_TOP_MARGIN, uno::makeAny( sal_Int32(0) ) );
+                SetDefaultParaProps( PROP_PARA_TOP_MARGIN, uno::makeAny( sal_Int32(0) ) );
             }
             m_pImpl->m_rDMapper.PopStyleSheetProperties();
             applyDefaults( true );
@@ -999,7 +1004,7 @@ void StyleSheetTable::ApplyStyleSheets( const FontTablePtr& rFontTable )
                     else if( bParaStyle )
                     {
                         // Paragraph styles that don't inherit from some parent need to apply the DocDefaults
-                        pEntry->pProperties->InsertProps( m_pImpl->m_pDefaultParaProps, /*bAllowOverwrite=*/false );
+                        pEntry->pProperties->InsertProps( m_pImpl->m_pDefaultParaProps, /*bOverwrite=*/false );
 
                         //now it's time to set the default parameters - for paragraph styles
                         //Fonts: Western first entry in font table
@@ -1467,13 +1472,13 @@ void StyleSheetTable::applyDefaults(bool bParaProperties)
         if( bParaProperties && m_pImpl->m_pDefaultParaProps.get())
         {
             // tdf#87533 LO will have different defaults here, depending on the locale. Import with documented defaults
-            m_pImpl->m_pDefaultParaProps->Insert(PROP_WRITING_MODE, uno::makeAny(sal_Int16(text::WritingMode_LR_TB)), /*bOverwrite=*/false);
-            m_pImpl->m_pDefaultParaProps->Insert(PROP_PARA_ADJUST, uno::makeAny(sal_Int16(style::ParagraphAdjust_LEFT)), false);
+            SetDefaultParaProps(PROP_WRITING_MODE, uno::makeAny(sal_Int16(text::WritingMode_LR_TB)));
+            SetDefaultParaProps(PROP_PARA_ADJUST, uno::makeAny(sal_Int16(style::ParagraphAdjust_LEFT)));
 
             // Widow/Orphan -> set both to two if not already set
             uno::Any aTwo = uno::makeAny(sal_Int8(2));
-            m_pImpl->m_pDefaultParaProps->Insert(PROP_PARA_WIDOWS, aTwo, /*bOverwrite=*/false);
-            m_pImpl->m_pDefaultParaProps->Insert(PROP_PARA_ORPHANS, aTwo, false);
+            SetDefaultParaProps(PROP_PARA_WIDOWS, aTwo);
+            SetDefaultParaProps(PROP_PARA_ORPHANS, aTwo);
 
             uno::Reference<style::XStyleFamiliesSupplier> xStylesSupplier(m_pImpl->m_xTextDocument, uno::UNO_QUERY);
             uno::Reference<container::XNameAccess> xStyleFamilies = xStylesSupplier->getStyleFamilies();
diff --git a/writerfilter/source/dmapper/StyleSheetTable.hxx b/writerfilter/source/dmapper/StyleSheetTable.hxx
index 686779acbd71..ee621093079e 100644
--- a/writerfilter/source/dmapper/StyleSheetTable.hxx
+++ b/writerfilter/source/dmapper/StyleSheetTable.hxx
@@ -102,6 +102,7 @@ public:
 
     OUString getOrCreateCharStyle( PropertyValueVector_t& rCharProperties, bool bAlwaysCreate );
 
+    void SetDefaultParaProps(PropertyIds eId, const css::uno::Any& rAny);
     PropertyMapPtr const & GetDefaultParaProps();
     /// Returns the default character properties.
     PropertyMapPtr const & GetDefaultCharProps();
commit 53a11d841a2860e51125ccc5deb09a338b892902
Author:     László Németh <nemeth at numbertext.org>
AuthorDate: Wed Oct 2 14:57:16 2019 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 10:59:00 2021 +0100

    tdf#81100 DOCX import: repeat header according to table style
    
    Table style based repeating table header wasn't repeated,
    only direct table formatting.
    
    (cherry picked from commit f9aac900ada3d507526eeeed5b51fc7a10ab4cae)
    
    Conflicts:
            sw/source/filter/ww8/docxattributeoutput.cxx
    
    Change-Id: I119e6d32bf22c6c85a84aa42ae4cd6c5f60166b2

diff --git a/sw/qa/extras/ooxmlexport/data/tdf81100.docx b/sw/qa/extras/ooxmlexport/data/tdf81100.docx
index 61038d190c7e..cd46100f0556 100644
Binary files a/sw/qa/extras/ooxmlexport/data/tdf81100.docx and b/sw/qa/extras/ooxmlexport/data/tdf81100.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 0736171dcfb0..e22b5ac0466f 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -938,6 +938,17 @@ DECLARE_OOXMLEXPORT_TEST(testTdf81100, "tdf81100.docx")
         return;
     // keep "repeat table header" setting of table styles
     assertXPath(pXmlDoc, "/w:styles/w:style/w:tblStylePr/w:trPr/w:tblHeader", 4);
+
+    xmlDocPtr pDump = parseLayoutDump();
+    CPPUNIT_ASSERT_EQUAL(3, getPages());
+
+    // table starts on page 1 and finished on page 2
+    // and it has got only a single repeating header line
+    assertXPath(pDump, "/root/page[2]/body/tab[1]", 1);
+    assertXPath(pDump, "/root/page[2]/body/tab[1]/row", 2);
+    assertXPath(pDump, "/root/page[3]/body/tab", 1);
+    if (!mbExported) // TODO export tblHeader=false
+        assertXPath(pDump, "/root/page[3]/body/tab/row", 1);
 }
 
 DECLARE_OOXMLEXPORT_TEST(testTdf121597TrackedDeletionOfMultipleParagraphs, "tdf121597.odt")
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 0438a1e54caf..69e73dc6474e 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -3573,7 +3573,7 @@ void DocxAttributeOutput::StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t c
     if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) )
         m_pSerializer->singleElementNS( XML_w, XML_tblHeader,
                FSNS( XML_w, XML_val ), "true",
-               FSEND );
+               FSEND ); // TODO to overwrite table style may need explicit false
 
     TableRowRedline( pTableTextNodeInfoInner );
     TableHeight( pTableTextNodeInfoInner );
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index 349cf28f4284..a0b077282885 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -49,6 +49,18 @@ using namespace ::com::sun::star;
 using namespace ::std;
 
 #define DEF_BORDER_DIST 190  //0,19cm
+#define CNF_FIRST_ROW               0x800
+#define CNF_LAST_ROW                0x400
+#define CNF_FIRST_COLUMN            0x200
+#define CNF_LAST_COLUMN             0x100
+#define CNF_ODD_VBAND               0x080
+#define CNF_EVEN_VBAND              0x040
+#define CNF_ODD_HBAND               0x020
+#define CNF_EVEN_HBAND              0x010
+#define CNF_FIRST_ROW_LAST_COLUMN   0x008
+#define CNF_FIRST_ROW_FIRST_COLUMN  0x004
+#define CNF_LAST_ROW_LAST_COLUMN    0x002
+#define CNF_LAST_ROW_FIRST_COLUMN   0x001
 
 DomainMapperTableHandler::DomainMapperTableHandler(
             css::uno::Reference<css::text::XTextAppendAndConvert> const& xText,
@@ -429,6 +441,10 @@ TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo
                 m_aTableProperties->dumpXml();
                 TagLogger::getInstance().endElement();
 #endif
+                // apply tblHeader setting of the table style
+                PropertyMapPtr pHeaderStyleProps = pTableStyle->GetProperties(CNF_FIRST_ROW);
+                if ( pHeaderStyleProps->getProperty(PROP_HEADER_ROW_COUNT) )
+                    m_aTableProperties->Insert(PROP_HEADER_ROW_COUNT, uno::makeAny( sal_Int32(1)), false);
             }
         }
 
@@ -632,19 +648,6 @@ TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo
     return pTableStyle;
 }
 
-#define CNF_FIRST_ROW               0x800
-#define CNF_LAST_ROW                0x400
-#define CNF_FIRST_COLUMN            0x200
-#define CNF_LAST_COLUMN             0x100
-#define CNF_ODD_VBAND               0x080
-#define CNF_EVEN_VBAND              0x040
-#define CNF_ODD_HBAND               0x020
-#define CNF_EVEN_HBAND              0x010
-#define CNF_FIRST_ROW_LAST_COLUMN   0x008
-#define CNF_FIRST_ROW_FIRST_COLUMN  0x004
-#define CNF_LAST_ROW_LAST_COLUMN    0x002
-#define CNF_LAST_ROW_FIRST_COLUMN   0x001
-
 CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges)
 {
 #ifdef DEBUG_WRITERFILTER
diff --git a/writerfilter/source/dmapper/DomainMapperTableManager.cxx b/writerfilter/source/dmapper/DomainMapperTableManager.cxx
index bc38207bbbc5..2ee0a892cbe5 100644
--- a/writerfilter/source/dmapper/DomainMapperTableManager.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableManager.cxx
@@ -226,7 +226,16 @@ bool DomainMapperTableManager::sprm(Sprm & rSprm)
                     insertTableProps(pPropMap);
                 }
                 else
+                {
+                    if ( nIntValue == 0 && m_nRow == 0 )
+                    {
+                        // explicit tblHeader=0 in the first row must overwrite table style
+                        TablePropertyMapPtr pPropMap( new TablePropertyMap );
+                        pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::makeAny(sal_Int32(0)));
+                        insertTableProps(pPropMap);
+                    }
                     m_nHeaderRepeat = -1;
+                }
                 if (nIntValue)
                 {
                     // Store the info that this is a header, we'll need that when we apply table styles.
diff --git a/writerfilter/source/dmapper/TblStylePrHandler.cxx b/writerfilter/source/dmapper/TblStylePrHandler.cxx
index cc4654d5de34..b4603e30482e 100644
--- a/writerfilter/source/dmapper/TblStylePrHandler.cxx
+++ b/writerfilter/source/dmapper/TblStylePrHandler.cxx
@@ -162,6 +162,7 @@ void TblStylePrHandler::lcl_sprm(Sprm & rSprm)
             break;
         case NS_ooxml::LN_CT_TrPrBase_tblHeader:
         {
+            m_pProperties->Insert( PROP_HEADER_ROW_COUNT, uno::makeAny(sal_Int32(1)));
             beans::PropertyValue aValue;
             aValue.Name = "tblHeader";
             aValue.Value <<= true;
commit 7792420601789c092c7a26262451b386a096d68b
Author:     László Németh <nemeth at numbertext.org>
AuthorDate: Thu Sep 26 12:43:02 2019 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 10:58:53 2021 +0100

    tdf#81100 DOCX: keep "repeat table header" table style setting
    
    during round trip by grab-bagging //tblStylePr/trPr/tblHeader.
    
    (cherry picked from commit 93ebf6a85f699e0594e05374ac37f8e582292d4f)
    
    Conflicts:
            sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
            sw/source/filter/ww8/docxtablestyleexport.cxx
    
    Change-Id: Id9d3150ca48031791aeda19c126bc4d4ac16fb8f

diff --git a/sw/qa/extras/ooxmlexport/data/tdf81100.docx b/sw/qa/extras/ooxmlexport/data/tdf81100.docx
new file mode 100644
index 000000000000..61038d190c7e
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf81100.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 018aa8ccfcb8..0736171dcfb0 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -931,6 +931,15 @@ DECLARE_OOXMLEXPORT_TEST(testTdf58944RepeatingTableHeader, "tdf58944-repeating-t
                          parseDump("/root/page[2]/body/tab/row[2]/cell[1]/txt/text()"));
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf81100, "tdf81100.docx")
+{
+    xmlDocPtr pXmlDoc = parseExport("word/styles.xml");
+    if (!pXmlDoc)
+        return;
+    // keep "repeat table header" setting of table styles
+    assertXPath(pXmlDoc, "/w:styles/w:style/w:tblStylePr/w:trPr/w:tblHeader", 4);
+}
+
 DECLARE_OOXMLEXPORT_TEST(testTdf121597TrackedDeletionOfMultipleParagraphs, "tdf121597.odt")
 {
     xmlDocPtr pXmlDoc = parseExport("word/document.xml");
diff --git a/sw/source/filter/ww8/docxtablestyleexport.cxx b/sw/source/filter/ww8/docxtablestyleexport.cxx
index 3a48ebd2f76a..a23b24215f1b 100644
--- a/sw/source/filter/ww8/docxtablestyleexport.cxx
+++ b/sw/source/filter/ww8/docxtablestyleexport.cxx
@@ -61,6 +61,8 @@ public:
     void tableStylePSpacing(uno::Sequence<beans::PropertyValue>& rSpacing);
     /// Export of w:tblPr.
     void tableStyleTablePr(uno::Sequence<beans::PropertyValue>& rTablePr);
+    /// Export of w:trPr.
+    void tableStyleTrPr(const uno::Sequence<beans::PropertyValue>& rTrPr);
     /// Export of w:tcPr.
     void tableStyleTcPr(uno::Sequence<beans::PropertyValue>& rTcPr);
     /// Export of w:tcBorders (and w:tblBorders).
@@ -552,6 +554,22 @@ void DocxTableStyleExport::Impl::tableStyleTablePr(uno::Sequence<beans::Property
     m_pSerializer->endElementNS(XML_w, XML_tblPr);
 }
 
+void DocxTableStyleExport::Impl::tableStyleTrPr(const uno::Sequence<beans::PropertyValue>& rTrPr)
+{
+    if (!rTrPr.hasElements())
+        return;
+
+    m_pSerializer->startElementNS(XML_w, XML_trPr, FSEND);
+
+    for (const auto& rProp : rTrPr)
+    {
+        if (rProp.Name == "tblHeader")
+            m_pSerializer->singleElementNS(XML_w, XML_tblHeader, FSEND);
+    }
+
+    m_pSerializer->endElementNS(XML_w, XML_trPr);
+}
+
 void DocxTableStyleExport::Impl::tableStyleTcPr(uno::Sequence<beans::PropertyValue>& rTcPr)
 {
     if (!rTcPr.hasElements())
@@ -589,7 +607,7 @@ void DocxTableStyleExport::Impl::tableStyleTableStylePr(
         return;
 
     OUString aType;
-    uno::Sequence<beans::PropertyValue> aPPr, aRPr, aTablePr, aTcPr;
+    uno::Sequence<beans::PropertyValue> aPPr, aRPr, aTablePr, aTrPr, aTcPr;
     for (sal_Int32 i = 0; i < rTableStylePr.getLength(); ++i)
     {
         if (rTableStylePr[i].Name == "type")
@@ -600,6 +618,8 @@ void DocxTableStyleExport::Impl::tableStyleTableStylePr(
             aRPr = rTableStylePr[i].Value.get<uno::Sequence<beans::PropertyValue>>();
         else if (rTableStylePr[i].Name == "tblPr")
             aTablePr = rTableStylePr[i].Value.get<uno::Sequence<beans::PropertyValue>>();
+        else if (rTableStylePr[i].Name == "trPr")
+            aTrPr = rTableStylePr[i].Value.get<uno::Sequence<beans::PropertyValue>>();
         else if (rTableStylePr[i].Name == "tcPr")
             aTcPr = rTableStylePr[i].Value.get<uno::Sequence<beans::PropertyValue>>();
     }
@@ -616,6 +636,7 @@ void DocxTableStyleExport::Impl::tableStyleTableStylePr(
         // Even if we have an empty container, write it out, as Word does.
         m_pSerializer->singleElementNS(XML_w, XML_tblPr, FSEND);
     }
+    tableStyleTrPr(aTrPr);
     tableStyleTcPr(aTcPr);
 
     m_pSerializer->endElementNS(XML_w, XML_tblStylePr);
diff --git a/writerfilter/source/dmapper/StyleSheetTable.cxx b/writerfilter/source/dmapper/StyleSheetTable.cxx
index 2d19297ad80f..08145c98ad2f 100644
--- a/writerfilter/source/dmapper/StyleSheetTable.cxx
+++ b/writerfilter/source/dmapper/StyleSheetTable.cxx
@@ -582,7 +582,7 @@ void StyleSheetTable::lcl_sprm(Sprm & rSprm)
                 pProperties->resolve(*pTblStylePrHandler);
                 StyleSheetEntry* pEntry = m_pImpl->m_pCurrentEntry.get();
                 TableStyleSheetEntry& rTableEntry = dynamic_cast<TableStyleSheetEntry&>(*pEntry);
-                rTableEntry.AppendInteropGrabBag(pTblStylePrHandler->getInteropGrabBag("tcPr"));
+                rTableEntry.AppendInteropGrabBag(pTblStylePrHandler->getInteropGrabBag((nSprmId == NS_ooxml::LN_CT_Style_tcPr) ? OUString("tcPr") : OUString("trPr")));
 
                 // This is a <w:tcPr> directly under <w:style>, so it affects the whole table.
                 rTableEntry.pProperties->InsertProps(pTblStylePrHandler->getProperties());
diff --git a/writerfilter/source/dmapper/TblStylePrHandler.cxx b/writerfilter/source/dmapper/TblStylePrHandler.cxx
index d2e5bb2054ce..cc4654d5de34 100644
--- a/writerfilter/source/dmapper/TblStylePrHandler.cxx
+++ b/writerfilter/source/dmapper/TblStylePrHandler.cxx
@@ -137,6 +137,7 @@ void TblStylePrHandler::lcl_sprm(Sprm & rSprm)
             bool bGrabBag = rSprm.getId() == NS_ooxml::LN_CT_PPrBase ||
                 rSprm.getId() == NS_ooxml::LN_EG_RPrBase ||
                 rSprm.getId() == NS_ooxml::LN_CT_TblPrBase ||
+                rSprm.getId() == NS_ooxml::LN_CT_TrPrBase ||
                 rSprm.getId() == NS_ooxml::LN_CT_TcPrBase;
             if (bGrabBag)
             {
@@ -151,12 +152,22 @@ void TblStylePrHandler::lcl_sprm(Sprm & rSprm)
                     aSavedGrabBag.push_back(getInteropGrabBag("rPr"));
                 else if (rSprm.getId() == NS_ooxml::LN_CT_TblPrBase)
                     aSavedGrabBag.push_back(getInteropGrabBag("tblPr"));
+                else if (rSprm.getId() == NS_ooxml::LN_CT_TrPrBase)
+                    aSavedGrabBag.push_back(getInteropGrabBag("trPr"));
                 else if (rSprm.getId() == NS_ooxml::LN_CT_TcPrBase)
                     aSavedGrabBag.push_back(getInteropGrabBag("tcPr"));
                 std::swap(m_aInteropGrabBag, aSavedGrabBag);
             }
         }
             break;
+        case NS_ooxml::LN_CT_TrPrBase_tblHeader:
+        {
+            beans::PropertyValue aValue;
+            aValue.Name = "tblHeader";
+            aValue.Value <<= true;
+            m_aInteropGrabBag.push_back(aValue);
+        }
+        break;
         default:
             // Tables specific properties have to handled here
             m_pTablePropsHandler->SetProperties( m_pProperties );
commit b110d14f4d7110ce4af2e938bde354b033615c88
Author:     László Németh <nemeth at numbertext.org>
AuthorDate: Tue Oct 8 13:25:10 2019 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 10:58:48 2021 +0100

    tdf#127814 DOCX: fix default paragraph margin in table cells
    
    when the table is started on a new page. Undefined
    w:before in w:docDefaults/w:pPrDefault resulted 0.5 cm
    paragraph top margin instead of 0 cm.
    
    (cherry picked from commit 88ddeed17d5a7caca407f892a094a3dcb0aff501)
    
    Conflicts:
            sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
    
    Change-Id: I94a2aa9e9c5fcee6443b74bb261c300c6a8e1303

diff --git a/sw/qa/extras/ooxmlexport/data/tdf127814.docx b/sw/qa/extras/ooxmlexport/data/tdf127814.docx
new file mode 100644
index 000000000000..10ed2348f166
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf127814.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
index 9aa780d01185..cd1665489db5 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
@@ -472,6 +472,15 @@ DECLARE_OOXMLEXPORT_TEST(testTdf119054, "tdf119054.docx")
     assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "line", "240");
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf127814, "tdf127814.docx")
+{
+    // Paragraph top margin was 0 in a table started on a new page
+    xmlDocPtr pXmlDoc = parseExport();
+    if (!pXmlDoc)
+        return;
+    assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p/w:pPr/w:spacing", "before", "0");
+}
+
 DECLARE_OOXMLEXPORT_TEST(testFdo69636, "fdo69636.docx")
 {
     /*
diff --git a/writerfilter/source/dmapper/StyleSheetTable.cxx b/writerfilter/source/dmapper/StyleSheetTable.cxx
index 957cdbedf356..2d19297ad80f 100644
--- a/writerfilter/source/dmapper/StyleSheetTable.cxx
+++ b/writerfilter/source/dmapper/StyleSheetTable.cxx
@@ -689,6 +689,11 @@ void StyleSheetTable::lcl_sprm(Sprm & rSprm)
         case NS_ooxml::LN_CT_DocDefaults_pPrDefault:
             m_pImpl->m_rDMapper.PushStyleSheetProperties( m_pImpl->m_pDefaultParaProps );
             resolveSprmProps( m_pImpl->m_rDMapper, rSprm );
+            if ( nSprmId == NS_ooxml::LN_CT_DocDefaults_pPrDefault && m_pImpl->m_pDefaultParaProps.get() &&
+                !m_pImpl->m_pDefaultParaProps->isSet( PROP_PARA_TOP_MARGIN ) )
+            {
+                m_pImpl->m_pDefaultParaProps->Insert( PROP_PARA_TOP_MARGIN, uno::makeAny( sal_Int32(0) ) );
+            }
             m_pImpl->m_rDMapper.PopStyleSheetProperties();
             applyDefaults( true );
             m_pImpl->m_bHasImportedDefaultParaProps = true;
commit 5194e808bbe987e634832344bd860b66777f60ca
Author:     László Németh <nemeth at numbertext.org>
AuthorDate: Fri Nov 15 15:44:55 2019 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 10:58:39 2021 +0100

    tdf#119054 DOCX: fix not table style based bottom margin
    
    in table cells, ie. using paragraph styles with bottom
    margin setting or direct paragraph formatting of bottom
    margin. Both of them overwrite the table style based
    bottom margin.
    
    (cherry picked from commit 6100909c84550036932d031f4d2f652e158a1a0a)
    
    Conflicts:
            writerfilter/source/dmapper/DomainMapperTableHandler.cxx
    
    Change-Id: I527b16c24fe47df8412291089ff86fadd3f9430b

diff --git a/sw/qa/extras/ooxmlexport/data/tdf119054.docx b/sw/qa/extras/ooxmlexport/data/tdf119054.docx
new file mode 100644
index 000000000000..9c3657c24a97
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf119054.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
index 910b14b00bb9..9aa780d01185 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
@@ -460,6 +460,18 @@ DECLARE_OOXMLEXPORT_TEST(testTdf128752, "tdf128752.docx")
     assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "after", "0");
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf119054, "tdf119054.docx")
+{
+    xmlDocPtr pXmlDoc = parseExport();
+    if (!pXmlDoc)
+        return;
+    // Don't overwrite before and after spacing of Heading2 by table style
+    assertXPathNoAttribute(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "before");
+    assertXPathNoAttribute(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "after");
+    // Use table style based single line spacing instead of the docDefaults' 254
+    assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "line", "240");
+}
+
 DECLARE_OOXMLEXPORT_TEST(testFdo69636, "fdo69636.docx")
 {
     /*
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index cbb46c29145a..349cf28f4284 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -807,6 +807,9 @@ CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(Tabl
                 // Remove properties from style/row that aren't allowed in cells
                 pAllCellProps->Erase( PROP_HEADER_ROW_COUNT );
                 pAllCellProps->Erase( PROP_TBL_HEADER );
+                // Remove paragraph properties from style/row that paragraph style can overwrite
+                pAllCellProps->Erase( PROP_PARA_BOTTOM_MARGIN );
+                pAllCellProps->Erase( PROP_PARA_LINE_SPACING );
 
                 // Then add the cell properties
                 pAllCellProps->InsertProps(*aCellIterator);
@@ -1010,6 +1013,36 @@ css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTabl
     return aRowProperties;
 }
 
+// table style has got bigger precedence than docDefault style,
+// but lower precedence than the paragraph styles and direct paragraph formatting
+void DomainMapperTableHandler::ApplyParaProperty(css::beans::PropertyValues aTableProperties, PropertyIds eId)
+{
+    OUString sPropertyName = getPropertyName(eId);
+    auto pTableProp = std::find_if(aTableProperties.begin(), aTableProperties.end(),
+        [&](const beans::PropertyValue& rProp) { return rProp.Name == sPropertyName; });
+    if (pTableProp != aTableProperties.end())
+    {
+        uno::Any aValue = pTableProp->Value;
+        for (const auto& rParaProp : m_rDMapper_Impl.m_aParagraphsToEndTable)
+        {
+            // there is no direct paragraph formatting
+            if (!rParaProp.m_pPropertyMap->isSet(eId))
+            {
+                OUString sParaStyleName;
+                rParaProp.m_rPropertySet->getPropertyValue("ParaStyleName") >>= sParaStyleName;
+                StyleSheetEntryPtr pEntry = m_rDMapper_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sParaStyleName);
+                uno::Any aMargin = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, pEntry, true, true);
+                uno::Any aMarginDocDefault = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, nullptr, true, true);
+                // use table style only when 1) both values are empty (no docDefault and paragraph style definitions) or
+                // 2) both non-empty values are equal (docDefault paragraph properties are copied to the base paragraph style during import)
+                // TODO check the case, when two parent styles modify the docDefault and the last one set back the docDefault value
+                if (aMargin == aMarginDocDefault)
+                    rParaProp.m_rPropertySet->setPropertyValue(sPropertyName, aValue);
+            }
+        }
+    }
+}
+
 void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart)
 {
 #ifdef DEBUG_WRITERFILTER
@@ -1109,16 +1142,8 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
                 }
 
                 // OOXML table style may container paragraph properties, apply these now.
-                for (int i = 0; i < aTableInfo.aTableProperties.getLength(); ++i)
-                {
-                    if (aTableInfo.aTableProperties[i].Name == "ParaBottomMargin")
-                    {
-                        uno::Any aBottomMargin = aTableInfo.aTableProperties[i].Value;
-
-                        for (const auto& rParaProp : m_rDMapper_Impl.m_aPendingParaProp )
-                            rParaProp->setPropertyValue("ParaBottomMargin", aBottomMargin );
-                    }
-                }
+                ApplyParaProperty(aTableInfo.aTableProperties, PROP_PARA_BOTTOM_MARGIN);
+                ApplyParaProperty(aTableInfo.aTableProperties, PROP_PARA_LINE_SPACING);
             }
         }
         catch ( const lang::IllegalArgumentException &e )
@@ -1198,7 +1223,7 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
     m_aCellProperties.clear();
     m_aRowProperties.clear();
     m_bHadFootOrEndnote = false;
-    m_rDMapper_Impl.m_aPendingParaProp.clear();
+    m_rDMapper_Impl.m_aParagraphsToEndTable.clear();
 
 #ifdef DEBUG_WRITERFILTER
     TagLogger::getInstance().endElement();
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
index 7a45afa5c0b9..16d2a0cc37cc 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
@@ -90,6 +90,9 @@ public:
        @param pProps  properties of the table
      */
     void startTable(const TablePropertyMapPtr& pProps);
+
+    void ApplyParaProperty(css::beans::PropertyValues aTableProperties, PropertyIds eId);
+
     /// Handle end of table.
     void endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart);
     /**
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 9de2d6f46e69..2489f50ba94e 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -704,9 +704,9 @@ const OUString DomainMapper_Impl::GetDefaultParaStyleName()
     return m_sDefaultParaStyleName;
 }
 
-uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, const bool bStyles)
+uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara)
 {
-    while( bStyles && pEntry.get( ) )
+    while(pEntry.get( ) )
     {
         if(pEntry->pProperties)
         {
@@ -1526,6 +1526,15 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con
                 }
 
                 css::uno::Reference<css::beans::XPropertySet> xParaProps(xTextRange, uno::UNO_QUERY);
+
+                // table style has got bigger precedence than docDefault style
+                // collect these pending paragraph properties to process in endTable()
+                if (xParaProps && m_nTableDepth > 0)
+                {
+                    TableParagraph aPending{pParaContext, xParaProps};
+                    m_aParagraphsToEndTable.push_back(aPending);
+                }
+
                 // tdf#118521 set paragraph top or bottom margin based on the paragraph style
                 // if we already set the other margin with direct formatting
                 if (xParaProps)
@@ -1546,16 +1555,7 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con
                         {
                             uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_BOTTOM_MARGIN);
                             if ( aMargin != uno::Any() )
-                            {
                                 xParaProps->setPropertyValue("ParaBottomMargin", aMargin);
-
-                                // table style has got bigger precedence than docDefault style
-                                // collect these pending paragraph properties to process in endTable()
-                                // TODO check the case, when two parent styles modify the docDefault and the last one set back the docDefault value
-                                uno::Any aMarginDocDefault = GetPropertyFromStyleSheet(PROP_PARA_BOTTOM_MARGIN, nullptr, true, true, false);
-                                if ( m_nTableDepth > 0 && aMargin == aMarginDocDefault )
-                                    m_aPendingParaProp.push_back(xParaProps);
-                            }
                         }
                         if ( !bContextSet )
                         {
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index c07bda028d86..cf681ea34deb 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -397,6 +397,13 @@ struct SymbolData
     { }
 };
 
+/// Information about a paragraph to be finished after a table end.
+struct TableParagraph
+{
+    PropertyMapPtr m_pPropertyMap;
+    css::uno::Reference<css::beans::XPropertySet> m_rPropertySet;
+};
+
 class DomainMapper;
 class DomainMapper_Impl final
 {
@@ -709,7 +716,7 @@ public:
     const OUString  GetDefaultParaStyleName();
 
     // specified style - including inherited properties. Indicate whether paragraph defaults should be checked.
-    css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, const bool bStyles = true);
+    css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara);
     // current paragraph style - including inherited properties
     css::uno::Any GetPropertyFromParaStyleSheet(PropertyIds eId);
     // context's character style - including inherited properties
@@ -1006,7 +1013,7 @@ public:
     void ClearPreviousParagraph();
 
     /// Table paragraph properties may need style update based on table style
-    std::vector<css::uno::Reference<css::beans::XPropertySet>> m_aPendingParaProp;
+    std::vector<TableParagraph> m_aParagraphsToEndTable;
 
 private:
     void PushPageHeaderFooter(bool bHeader, SectionPropertyMap::PageType eType);
commit 8bccf056883b8c178b11f0db0aa0c4843ee7418a
Author:     László Németh <nemeth at numbertext.org>
AuthorDate: Fri Nov 15 11:58:01 2019 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 10:50:28 2021 +0100

    DOCX: clean-up paragraph bottom handling of table style
    
    Revert of commit 17e904ed66c3caf87e658b9d3a18d7b13f4a0b52
    ("bnc#816593 DOCX filter: import paragraph spacing from table style),
    keeping only the working unit test.
    
    (cherry picked from commit 00eeb7b3f765a51f51f7911a116982fbfb83efb7)
    
    Conflicts:
            writerfilter/source/dmapper/DomainMapperTableHandler.cxx
    
    Change-Id: I735744aadb071ef2f0d939cb637d83cfc5716fe4

diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index 61e088bfa4c1..cbb46c29145a 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -19,8 +19,6 @@
 #include "DomainMapperTableHandler.hxx"
 #include "DomainMapper_Impl.hxx"
 #include "StyleSheetTable.hxx"
-#include <com/sun/star/beans/XPropertyState.hpp>
-#include <com/sun/star/container/XEnumerationAccess.hpp>
 #include <com/sun/star/table/TableBorderDistances.hpp>
 #include <com/sun/star/table/TableBorder.hpp>
 #include <com/sun/star/table/BorderLine2.hpp>
@@ -1012,23 +1010,6 @@ css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTabl
     return aRowProperties;
 }
 
-// Apply paragraph property to each paragraph within a cell.
-static void lcl_ApplyCellParaProps(uno::Reference<table::XCell> const& xCell,
-        const uno::Any& rBottomMargin)
-{
-    uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCell, uno::UNO_QUERY);
-    uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
-    while (xEnumeration->hasMoreElements())
-    {
-        uno::Reference<beans::XPropertySet> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
-        uno::Reference<beans::XPropertyState> xPropertyState(xParagraph, uno::UNO_QUERY);
-        // Don't apply in case direct formatting is already present.
-        // TODO: probably paragraph style has priority over table style here.
-        if (xPropertyState.is() && xPropertyState->getPropertyState("ParaBottomMargin") == beans::PropertyState_DEFAULT_VALUE)
-            xParagraph->setPropertyValue("ParaBottomMargin", rBottomMargin);
-    }
-}
-
 void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart)
 {
 #ifdef DEBUG_WRITERFILTER
@@ -1132,21 +1113,10 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
                 {
                     if (aTableInfo.aTableProperties[i].Name == "ParaBottomMargin")
                     {
-                        uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY);
                         uno::Any aBottomMargin = aTableInfo.aTableProperties[i].Value;
-                        sal_Int32 nRows = aCellProperties.getLength();
-
-                    for (const auto& rParaProp : m_rDMapper_Impl.m_aPendingParaProp )
-                        rParaProp->setPropertyValue("ParaBottomMargin", aBottomMargin );
 
-                        for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
-                        {
-                            const uno::Sequence< beans::PropertyValues > aCurrentRow = aCellProperties[nRow];
-                            sal_Int32 nCells = aCurrentRow.getLength();
-                            for (sal_Int32 nCell = 0; nCell < nCells; ++nCell)
-                                lcl_ApplyCellParaProps(xCellRange->getCellByPosition(nCell, nRow), aBottomMargin);
-                        }
-                        break;
+                        for (const auto& rParaProp : m_rDMapper_Impl.m_aPendingParaProp )
+                            rParaProp->setPropertyValue("ParaBottomMargin", aBottomMargin );
                     }
                 }
             }
commit 1b6a7ca11869f0a80c08df4a7d1caac0c3ff02e5
Author:     László Németh <nemeth at numbertext.org>
AuthorDate: Fri Nov 15 10:37:42 2019 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 10:50:20 2021 +0100

    tdf#128752 DOCX: fix partial direct paragraph spacing in tables
    
    When direct formatting of a table paragraph set only top margin,
    but not the bottom margin, also there was no paragraph style setting
    for the bottom margin, the paragraph was imported using docDefault
    bottom spacing instead of the table style bottom spacing.
    
    (cherry picked from commit d8f3f8ecd9e6304f3a98ab03fae6bc545893f782)
    
    Conflicts:
            sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
            writerfilter/source/dmapper/DomainMapper_Impl.hxx
    
    Change-Id: Ib7f5f80dd2485a0fd4ab8e0645b7d730a7ec3c5c

diff --git a/sw/qa/extras/ooxmlexport/data/tdf128752.docx b/sw/qa/extras/ooxmlexport/data/tdf128752.docx
new file mode 100644
index 000000000000..0e49291414d7
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf128752.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
index 94121b7e22b4..910b14b00bb9 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
@@ -451,6 +451,15 @@ DECLARE_OOXMLEXPORT_TEST(testTableFloatingMargins, "table-floating-margins.docx"
     assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p/w:pPr/w:spacing", "after", "0");
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf128752, "tdf128752.docx")
+{
+    // Paragraph bottom margin was 200, docDefault instead of table style setting
+    xmlDocPtr pXmlDoc = parseExport();
+    if (!pXmlDoc)
+        return;
+    assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:p[1]/w:pPr/w:spacing", "after", "0");
+}
+
 DECLARE_OOXMLEXPORT_TEST(testFdo69636, "fdo69636.docx")
 {
     /*
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index 67f33911f1c4..61e088bfa4c1 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -1135,6 +1135,10 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
                         uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY);
                         uno::Any aBottomMargin = aTableInfo.aTableProperties[i].Value;
                         sal_Int32 nRows = aCellProperties.getLength();
+
+                    for (const auto& rParaProp : m_rDMapper_Impl.m_aPendingParaProp )
+                        rParaProp->setPropertyValue("ParaBottomMargin", aBottomMargin );
+
                         for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
                         {
                             const uno::Sequence< beans::PropertyValues > aCurrentRow = aCellProperties[nRow];
@@ -1224,6 +1228,7 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
     m_aCellProperties.clear();
     m_aRowProperties.clear();
     m_bHadFootOrEndnote = false;
+    m_rDMapper_Impl.m_aPendingParaProp.clear();
 
 #ifdef DEBUG_WRITERFILTER
     TagLogger::getInstance().endElement();
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index ceb6d3fba0f3..9de2d6f46e69 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -704,9 +704,9 @@ const OUString DomainMapper_Impl::GetDefaultParaStyleName()
     return m_sDefaultParaStyleName;
 }
 
-uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara)
+uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, const bool bStyles)
 {
-    while(pEntry.get( ) )
+    while( bStyles && pEntry.get( ) )
     {
         if(pEntry->pProperties)
         {
@@ -1546,7 +1546,16 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con
                         {
                             uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_BOTTOM_MARGIN);
                             if ( aMargin != uno::Any() )
+                            {
                                 xParaProps->setPropertyValue("ParaBottomMargin", aMargin);
+
+                                // table style has got bigger precedence than docDefault style
+                                // collect these pending paragraph properties to process in endTable()
+                                // TODO check the case, when two parent styles modify the docDefault and the last one set back the docDefault value
+                                uno::Any aMarginDocDefault = GetPropertyFromStyleSheet(PROP_PARA_BOTTOM_MARGIN, nullptr, true, true, false);
+                                if ( m_nTableDepth > 0 && aMargin == aMarginDocDefault )
+                                    m_aPendingParaProp.push_back(xParaProps);
+                            }
                         }
                         if ( !bContextSet )
                         {
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 258398255fb1..c07bda028d86 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -709,7 +709,7 @@ public:
     const OUString  GetDefaultParaStyleName();
 
     // specified style - including inherited properties. Indicate whether paragraph defaults should be checked.
-    css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara);
+    css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, const bool bStyles = true);
     // current paragraph style - including inherited properties
     css::uno::Any GetPropertyFromParaStyleSheet(PropertyIds eId);
     // context's character style - including inherited properties
@@ -1005,6 +1005,9 @@ public:
     /// start/end node.
     void ClearPreviousParagraph();
 
+    /// Table paragraph properties may need style update based on table style
+    std::vector<css::uno::Reference<css::beans::XPropertySet>> m_aPendingParaProp;
+
 private:
     void PushPageHeaderFooter(bool bHeader, SectionPropertyMap::PageType eType);
     std::vector<css::uno::Reference< css::drawing::XShape > > m_vTextFramesForChaining ;
commit a56f3177fba49f2ead26f8f5a54e6d1c626d623b
Author:     Justin Luth <justin.luth at collabora.com>
AuthorDate: Fri Oct 4 11:47:57 2019 +0300
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Mar 5 10:50:15 2021 +0100

    related tdf#99602 writerfilter TODO: subscript - use CharStyle fontsize
    
    GetAnyProperty was missing a check for character style properties.
    
    This patch depends on commit 875793d841165aaaaefa2c34b855e8f0f8a8c214
    related tdf#99602 writerfilter TODO: subscript - use ParaStyle fontsize
    
    and on commit 5e97d1a57717f8dbf69b987d2bda8616972eec52
    NFC writerfilter: preparation for adding CharProps to GetAnyProperty
    
    (cherry picked from commit 9b8052bba91ed616de77006cd0d3dee3965caece)
    
    Conflicts:
            sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
    
    Change-Id: I4e28589917e41fa545d5aab05f97a67502486136

diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 6df5464e436b..ceb6d3fba0f3 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -704,7 +704,7 @@ const OUString DomainMapper_Impl::GetDefaultParaStyleName()
     return m_sDefaultParaStyleName;
 }
 
-uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bPara)
+uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara)
 {
     while(pEntry.get( ) )
     {
@@ -730,7 +730,7 @@ uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleShee
         pEntry = pNewEntry;
     }
     // not found in style, try the document's DocDefault properties
-    if ( bPara )
+    if ( bDocDefaults && bPara )
     {
         const PropertyMapPtr& pDefaultParaProps = GetStyleSheetTable()->GetDefaultParaProps();
         if ( pDefaultParaProps )
@@ -740,7 +740,7 @@ uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleShee
                 return aProperty->second;
         }
     }
-    if ( isCharacterProperty(eId) )
+    if ( bDocDefaults && isCharacterProperty(eId) )
     {
         const PropertyMapPtr& pDefaultCharProps = GetStyleSheetTable()->GetDefaultCharProps();
         if ( pDefaultCharProps )
@@ -760,17 +760,40 @@ uno::Any DomainMapper_Impl::GetPropertyFromParaStyleSheet(PropertyIds eId)
         pEntry = GetStyleSheetTable()->GetCurrentEntry();
     else
         pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(GetCurrentParaStyleName());
-    return GetPropertyFromStyleSheet(eId, pEntry, /*bPara=*/true);
+    return GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/true, /*bPara=*/true);
+}
+
+uno::Any DomainMapper_Impl::GetPropertyFromCharStyleSheet(PropertyIds eId, const PropertyMapPtr& rContext)
+{
+    if ( m_bInStyleSheetImport || eId == PROP_CHAR_STYLE_NAME || !isCharacterProperty(eId) )
+        return uno::Any();
+
+    StyleSheetEntryPtr pEntry;
+    OUString sCharStyleName;

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list