include/xmloff/xmltoken.hxx                                 |    2 
 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng |    8 
 sw/Library_msword.mk                                        |    1 
 sw/source/core/layout/fly.cxx                               |   56 
 sw/source/filter/ww8/docxattributeoutput.cxx                |  842 -----------
 sw/source/filter/ww8/docxattributeoutput.hxx                |   26 
 sw/source/filter/ww8/docxtableexport.cxx                    |  869 ++++++++++++
 xmloff/qa/unit/data/floattable.fodt                         |   31 
 xmloff/qa/unit/text.cxx                                     |   51 
 xmloff/source/core/xmltoken.cxx                             |    2 
 xmloff/source/text/XMLTextFrameContext.cxx                  |   10 
 xmloff/source/text/txtparae.cxx                             |    6 
 xmloff/source/token/tokens.txt                              |    1 
 13 files changed, 1081 insertions(+), 824 deletions(-)

New commits:
commit 5b2d34e8e0869a9c8a7581b628c3f93e45e7d68f
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Feb 27 08:21:01 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Mar 6 09:51:23 2023 +0100

    sw floattable: allow extra space on top of child content in 
SwFlyFrame::Grow_()
    
    Once a floating table with a single cell wanted to span over two pages
    ("split row"), SwTextFrame::SplitFrame() was called, but then the table
    split failed because everything moved to the next page.
    
    The reason for this was that we tried to grow the text frame (and all of
    its parents: cell, row, table, fly), but a non-test growing a fly
    normally just works by recalculating its size based on the content,
    ignoring what growth is wanted.
    
    This is fine in the non-split fly case, but we do want to increase the
    size of a split fly to control what content remains in the current fly
    frame and what goes to a follow fly frame.
    
    Fix this by extending SwFlyFrame::Grow_() so it grows more than just its
    own content in the split fly case if the request wanted that.  Now a
    single row can split, but the position of the follow fly frame is still
    bad.
    
    (cherry picked from commit 7677e16217349a0a0e94edb6a90b00089432c6ce)
    
    Change-Id: I1c6aa61996dab058c291331a795627d3fae1bca8

diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
index efeb2cea8a5c..6918f630f511 100644
--- a/sw/source/core/layout/fly.cxx
+++ b/sw/source/core/layout/fly.cxx
@@ -2054,7 +2054,37 @@ SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
             InvalidatePos();
             if ( bOldLock )
                 Lock();
-            const SwRect aNew( GetObjRectWithSpaces() );
+            SwRect aNew(GetObjRectWithSpaces());
+            if (IsFlySplitAllowed() && aNew.Height() - aOld.Height() < nDist)
+            {
+                // We are allowed to split and the actual growth is less than 
the requested growth.
+                const SwFrame* pAnchor = GetAnchorFrame();
+                if (SwFrame* pAnchorChar = FindAnchorCharFrame())
+                {
+                    pAnchor = pAnchorChar;
+                }
+                const SwFrame* pAnchorUpper = pAnchor ? pAnchor->GetUpper() : 
nullptr;
+                if (pAnchorUpper)
+                {
+                    SwTwips nDeadline = aRectFnSet.GetPrtBottom(*pAnchorUpper);
+                    SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
+                    SwTwips nBottom = nTop + 
aRectFnSet.GetHeight(getFrameArea());
+                    SwTwips nMaxGrow = nDeadline - nBottom;
+                    if (nDist > nMaxGrow)
+                    {
+                        // The requested growth is more than what we can 
provide, limit it.
+                        nDist = nMaxGrow;
+                    }
+                    // Grow & invalidate the size.
+                    SwTwips nRemaining = nDist - (aNew.Height() - 
aOld.Height());
+                    {
+                        SwFrameAreaDefinition::FrameAreaWriteAccess 
aFrm(*this);
+                        aRectFnSet.AddBottom(aFrm, nRemaining);
+                    }
+                    InvalidateObjRectWithSpaces();
+                    aNew = GetObjRectWithSpaces();
+                }
+            }
             if ( aOld != aNew )
                 ::Notify( this, FindPageFrame(), aOld );
             return aRectFnSet.GetHeight(aNew)-aRectFnSet.GetHeight(aOld);
commit 52953d35e2f65894fcc879d6c0c2ca5231a22f46
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Feb 24 08:21:22 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Mar 6 09:51:23 2023 +0100

    sw floattable: don't promise infinite growth in SwFlyFrame::Grow_()
    
    In case the document has a single cell in the floating table with
    multiple lines, we may need to split the cell, but currently we don't.
    
    One problem is that SwTabFrame::MakeAll() for the master table gets an
    error from Split(). Digging deeper, SwTextFrame::SplitFrame() is not
    called at all, because SwTextFrame::FormatAdjust() thinks that the
    content will fit, because SwTextFrameBreak::IsInside() says so. This
    happens because it tries to do a test grow on the fly frame, and
    that promises to grow as much as wanted.
    
    Infinite grow for fly frames is reasonable by default, but we want to
    limit the size for split flys, so a follow fly will be created. Fix the
    problem by improving SwFlyFrame::Grow_() to handle the
    IsFlySplitAllowed() case explicitly. Now we split the text frame, which
    is needed but not enough to split the entire fly -> table -> row -> cell
    -> text frame hierarchy.
    
    SwSectionFrame::Grow_() has a similar limit in place already.
    
    (cherry picked from commit 6068ae5df9da179e1d187e27117a9d761116f968)
    
    Change-Id: Ie07362a8dc3aa8c4fbb69eaf7e35717ba79b99a0

diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
index 91c0f0549680..efeb2cea8a5c 100644
--- a/sw/source/core/layout/fly.cxx
+++ b/sw/source/core/layout/fly.cxx
@@ -2059,6 +2059,30 @@ SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
                 ::Notify( this, FindPageFrame(), aOld );
             return aRectFnSet.GetHeight(aNew)-aRectFnSet.GetHeight(aOld);
         }
+        else
+        {
+            // We're in test mode. Don't promise infinite growth for split 
flys, rather limit the
+            // max size to the bottom of the upper.
+            const SwFrame* pAnchor = GetAnchorFrame();
+            if (SwFrame* pAnchorChar = FindAnchorCharFrame())
+            {
+                pAnchor = pAnchorChar;
+            }
+            const SwFrame* pAnchorUpper = pAnchor ? pAnchor->GetUpper() : 
nullptr;
+            if (pAnchorUpper && IsFlySplitAllowed())
+            {
+                SwTwips nDeadline = aRectFnSet.GetPrtBottom(*pAnchorUpper);
+                SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
+                SwTwips nBottom = nTop + aRectFnSet.GetHeight(getFrameArea());
+                // Calculate max grow and compare to the requested growth, 
adding to nDist may
+                // overflow when it's LONG_MAX.
+                SwTwips nMaxGrow = nDeadline - nBottom;
+                if (nDist > nMaxGrow)
+                {
+                    nDist = nMaxGrow;
+                }
+            }
+        }
         return nDist;
     }
     return 0;
commit c364afe8e58efc33a39615553d62f4875e2888bd
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Feb 23 08:21:46 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Mar 6 09:51:17 2023 +0100

    sw floattable: teach the ODT filter about SwFormatFlySplit
    
    Map the IsSplitAllowed text frame property to <draw:frame
    loext:may-break-between-pages="...">. This is meant to be always a
    direct formatting, so doesn't go to the style / autostyle.
    
    (cherry picked from commit 2da16ff9f018fae68c53a801e5a234dafc2ebcec)
    
    Change-Id: I439fe372271e4b7db511b5e2150e2a3c2e0acf25

diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index e33a39deb449..96ffea048732 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -3510,6 +3510,8 @@ namespace xmloff::token {
 
         XML_FILL_USE_SLIDE_BACKGROUND,
 
+        XML_MAY_BREAK_BETWEEN_PAGES,
+
         XML_TOKEN_END
     };
 
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index 384b3cde05f3..e2d81480318c 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -3352,13 +3352,19 @@ 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
     </rng:optional>
   </rng:define>
 
-  <!-- https://issues.oasis-open.org/browse/OFFICE-4136 -->
   <rng:define name="draw-frame-attlist" combine="interleave">
+    <!-- https://issues.oasis-open.org/browse/OFFICE-4136 -->
     <rng:optional>
       <rng:attribute name="loext:decorative">
         <rng:ref name="boolean"/>
       </rng:attribute>
     </rng:optional>
+    <!-- TODO(vmiklos) no proposal for multi-page floating tables -->
+    <rng:optional>
+      <rng:attribute name="loext:may-break-between-pages">
+        <rng:ref name="boolean"/>
+      </rng:attribute>
+    </rng:optional>
   </rng:define>
 
 </rng:grammar>
diff --git a/xmloff/qa/unit/data/floattable.fodt 
b/xmloff/qa/unit/data/floattable.fodt
new file mode 100644
index 000000000000..927d14b757e9
--- /dev/null
+++ b/xmloff/qa/unit/data/floattable.fodt
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"
 office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+  <office:styles>
+    <style:style style:name="Frame" style:family="graphic"/>
+  </office:styles>
+  <office:automatic-styles>
+    <style:style style:name="Table1" style:family="table">
+      <style:table-properties style:width="6.743cm"/>
+    </style:style>
+    <style:style style:name="Table1.A" style:family="table-column">
+      <style:table-column-properties style:column-width="6.743cm"/>
+    </style:style>
+    <style:style style:name="Table1.1" style:family="table-row">
+      <style:table-row-properties style:min-row-height="3.196cm"/>
+    </style:style>
+    <style:style style:name="fr1" style:family="graphic" 
style:parent-style-name="Frame">
+      <style:graphic-properties fo:margin-left="0.318cm" 
fo:margin-right="0.318cm" fo:margin-top="0cm" fo:margin-bottom="0cm" 
style:vertical-pos="from-top" style:vertical-rel="paragraph" 
style:horizontal-pos="from-left" style:horizontal-rel="paragraph" 
fo:padding="0cm" fo:border="none"/>
+    </style:style>
+    <style:page-layout style:name="pm1">
+      <style:page-layout-properties fo:page-width="21.59cm" 
fo:page-height="10.941cm" fo:margin-top="2.54cm" fo:margin-bottom="2.54cm" 
fo:margin-left="2.54cm" fo:margin-right="2.54cm"/>
+    </style:page-layout>
+  </office:automatic-styles>
+  <office:master-styles>
+    <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+  </office:master-styles>
+  <office:body>
+    <office:text>
+      <text:p><draw:frame draw:style-name="fr1" draw:name="Frame1" 
text:anchor-type="paragraph" svg:x="-0.009cm" svg:y="0.002cm" 
svg:width="6.743cm" draw:z-index="0" 
loext:may-break-between-pages="true"><draw:text-box 
fo:min-height="0cm"><table:table table:name="Table1" 
table:style-name="Table1"><table:table-column 
table:style-name="Table1.A"/><table:table-row 
table:style-name="Table1.1"><table:table-cell 
office:value-type="string"><text:p>A1</text:p></table:table-cell></table:table-row><table:table-row
 table:style-name="Table1.1"><table:table-cell 
office:value-type="string"><text:p>A2</text:p></table:table-cell></table:table-row></table:table></draw:text-box></draw:frame>anchor
 text</text:p>
+    </office:text>
+  </office:body>
+</office:document>
diff --git a/xmloff/qa/unit/text.cxx b/xmloff/qa/unit/text.cxx
index 5dfd52010a09..ded7196be09b 100644
--- a/xmloff/qa/unit/text.cxx
+++ b/xmloff/qa/unit/text.cxx
@@ -17,6 +17,7 @@
 #include <com/sun/star/text/TextContentAnchorType.hpp>
 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/text/XTextFramesSupplier.hpp>
 
 #include <comphelper/propertysequence.hxx>
 #include <comphelper/propertyvalue.hxx>
@@ -956,6 +957,56 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testThemeExport)
                 "#c0c0c0");
 }
 
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testFloatingTableExport)
+{
+    // Given a document with a floating table:
+    mxComponent = loadFromDesktop("private:factory/swriter");
+    // Insert a table:
+    uno::Sequence<beans::PropertyValue> aArgs = {
+        comphelper::makePropertyValue("Rows", static_cast<sal_Int32>(1)),
+        comphelper::makePropertyValue("Columns", static_cast<sal_Int32>(1)),
+    };
+    dispatchCommand(mxComponent, ".uno:InsertTable", aArgs);
+    // Select it:
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    // Wrap in a fly:
+    aArgs = {
+        comphelper::makePropertyValue("AnchorType", 
static_cast<sal_uInt16>(0)),
+    };
+    dispatchCommand(mxComponent, ".uno:InsertFrame", aArgs);
+    // Mark it as a floating table:
+    uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xFrame(
+        xTextFramesSupplier->getTextFrames()->getByName("Frame1"), 
uno::UNO_QUERY);
+    xFrame->setPropertyValue("IsSplitAllowed", uno::Any(true));
+
+    // When saving to ODT:
+    save("writer8");
+
+    // Then make sure we write a floating table, not a textframe containing a 
table:
+    xmlDocUniquePtr pXmlDoc = parseExport("content.xml");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - XPath '//draw:frame' no attribute 'may-break-between-pages' exist
+    // i.e. no floating table was exported.
+    assertXPath(pXmlDoc, "//draw:frame", "may-break-between-pages", "true");
+}
+
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testFloatingTableImport)
+{
+    // Given a document with a floating table 
(loext:may-break-between-pages="true"), when importing
+    // that document:
+    loadFromURL(u"floattable.fodt");
+
+    // Then make sure that the matching text frame property is set:
+    uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xFrame(
+        xTextFramesSupplier->getTextFrames()->getByName("Frame1"), 
uno::UNO_QUERY);
+    bool bIsSplitAllowed = false;
+    // Without the accompanying fix in place, this test would have failed, the 
property was false.
+    xFrame->getPropertyValue("IsSplitAllowed") >>= bIsSplitAllowed;
+    CPPUNIT_ASSERT(bIsSplitAllowed);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index d1d2047a50b9..c9c00208edd0 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -3513,6 +3513,8 @@ namespace xmloff::token {
 
         TOKEN("fill-use-slide-background", XML_FILL_USE_SLIDE_BACKGROUND),
 
+        TOKEN("may-break-between-pages", XML_MAY_BREAK_BETWEEN_PAGES),
+
 #if OSL_DEBUG_LEVEL > 0
         { 0, nullptr, std::nullopt,               XML_TOKEN_END }
 #else
diff --git a/xmloff/source/text/XMLTextFrameContext.cxx 
b/xmloff/source/text/XMLTextFrameContext.cxx
index ad7ca31254bc..c045ea181680 100644
--- a/xmloff/source/text/XMLTextFrameContext.cxx
+++ b/xmloff/source/text/XMLTextFrameContext.cxx
@@ -371,6 +371,7 @@ class XMLTextFrameContext_Impl : public SvXMLImportContext
     bool    bOwnBase64Stream : 1;
     bool    mbMultipleContent : 1; // This context is created based on a 
multiple content (image)
     bool    m_isDecorative = false;
+    bool    m_isSplitAllowed = false;
 
     void Create();
 
@@ -688,6 +689,11 @@ void XMLTextFrameContext_Impl::Create()
         xPropSet->setPropertyValue("Decorative", uno::Any(true));
     }
 
+    if (m_isSplitAllowed && xPropSetInfo->hasPropertyByName("IsSplitAllowed"))
+    {
+        xPropSet->setPropertyValue("IsSplitAllowed", uno::Any(true));
+    }
+
     if( XML_TEXT_FRAME_OBJECT != nType  &&
         XML_TEXT_FRAME_OBJECT_OLE != nType  &&
         XML_TEXT_FRAME_APPLET != nType &&
@@ -1074,6 +1080,10 @@ XMLTextFrameContext_Impl::XMLTextFrameContext_Impl(
         case XML_ELEMENT(DRAW, XML_DECORATIVE):
             ::sax::Converter::convertBool(m_isDecorative, aIter.toString());
             break;
+        case XML_ELEMENT(LO_EXT, XML_MAY_BREAK_BETWEEN_PAGES):
+        case XML_ELEMENT(DRAW, XML_MAY_BREAK_BETWEEN_PAGES):
+            sax::Converter::convertBool(m_isSplitAllowed, aIter.toString());
+            break;
         default:
             SAL_INFO("xmloff", "unknown attribute " << 
SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << " value=" << 
aIter.toString());
         }
diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx
index 5f874c50c4cf..aa81727678c6 100644
--- a/xmloff/source/text/txtparae.cxx
+++ b/xmloff/source/text/txtparae.cxx
@@ -3016,6 +3016,12 @@ XMLShapeExportFlags 
XMLTextParagraphExport::addTextFrameAttributes(
         GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_DECORATIVE, 
XML_TRUE);
     }
 
+    if (xPropSetInfo->hasPropertyByName("IsSplitAllowed")
+        && rPropSet->getPropertyValue("IsSplitAllowed").get<bool>())
+    {
+        GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, 
XML_MAY_BREAK_BETWEEN_PAGES, XML_TRUE);
+    }
+
     return nShapeFeatures;
 }
 
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index e4a0a5fe7c36..48740a9fc020 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -3254,4 +3254,5 @@ plain-text
 alias
 tag
 fill-use-slide-background
+may-break-between-pages
 TOKEN_END_DUMMY
commit a82f24a0af1cc17432e0211795de4a4af8836a2b
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Feb 22 10:37:53 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Mar 6 09:38:30 2023 +0100

    DOCX export: move the bulk of the table export code to a new file
    
    docxattributeoutput.cxx was already more than 10k lines long, which
    means it's really slow to recompile it, compared to other files in the
    same directory.
    
    Split out some of the table handling functionality into a new
    docxtableexport.cxx, so touching that (floating) table code has much
    improved rebuild time when developing.
    
    (cherry picked from commit 3a8ecbc70320151cb7dde7d8f89dee67a7c6e3e5)
    
    Conflicts:
            sw/source/filter/ww8/docxattributeoutput.cxx
    
    Change-Id: I4fe94a572ae8f684c78799dedbaa346d9a1bdae9

diff --git a/sw/Library_msword.mk b/sw/Library_msword.mk
index bf1ed7d09322..526d25fd96e4 100644
--- a/sw/Library_msword.mk
+++ b/sw/Library_msword.mk
@@ -82,6 +82,7 @@ $(eval $(call gb_Library_add_exception_objects,msword,\
     sw/source/filter/ww8/docxexport \
     sw/source/filter/ww8/docxexportfilter \
     sw/source/filter/ww8/docxsdrexport \
+    sw/source/filter/ww8/docxtableexport \
     sw/source/filter/ww8/docxtablestyleexport \
     sw/source/filter/ww8/rtfattributeoutput \
     sw/source/filter/ww8/rtfexport \
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index f1485b5814f4..6da9a70f9881 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -34,7 +34,6 @@
 #include <fmtanchr.hxx>
 #include <breakit.hxx>
 #include <redline.hxx>
-#include <unocoll.hxx>
 #include <unoframe.hxx>
 #include <textboxhelper.hxx>
 #include <rdfhelper.hxx>
@@ -97,7 +96,6 @@
 #include <svx/xflgrit.hxx>
 #include <svx/svdouno.hxx>
 #include <svx/unobrushitemhelper.hxx>
-#include <svx/svdogrp.hxx>
 #include <svl/grabbagitem.hxx>
 #include <sfx2/sfxbasemodel.hxx>
 #include <tools/date.hxx>
@@ -113,7 +111,6 @@
 #include <flddropdown.hxx>
 #include <fmtclds.hxx>
 #include <fmtinfmt.hxx>
-#include <fmtrowsplt.hxx>
 #include <fmtline.hxx>
 #include <ftninfo.hxx>
 #include <htmltbl.hxx>
@@ -172,24 +169,6 @@ using namespace sw::util;
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::drawing;
 
-const sal_Int32 Tag_StartParagraph_1 = 1;
-const sal_Int32 Tag_StartParagraph_2 = 2;
-const sal_Int32 Tag_WriteSdtBlock = 3;
-const sal_Int32 Tag_StartParagraphProperties = 4;
-const sal_Int32 Tag_InitCollectedParagraphProperties = 5;
-const sal_Int32 Tag_StartRun_1 = 6;
-const sal_Int32 Tag_StartRun_2 = 7;
-const sal_Int32 Tag_StartRun_3 = 8;
-const sal_Int32 Tag_EndRun_1 = 9;
-const sal_Int32 Tag_EndRun_2 = 10;
-const sal_Int32 Tag_StartRunProperties = 11;
-const sal_Int32 Tag_InitCollectedRunProperties = 12;
-const sal_Int32 Tag_Redline_1 = 13;
-const sal_Int32 Tag_Redline_2 = 14;
-const sal_Int32 Tag_TableDefinition = 15;
-const sal_Int32 Tag_OutputFlyFrame = 16;
-const sal_Int32 Tag_StartSection = 17;
-
 namespace {
 
 class FFDataWriterHelper
@@ -320,7 +299,7 @@ static bool lcl_isOnelinerSdt(std::u16string_view rName)
     return rName == u"Title" || rName == u"Subtitle" || rName == u"Company";
 }
 
-static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& 
pAttrList, sal_Int32 nAttrs, ...)
+void 
DocxAttributeOutput::AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>&
 pAttrList, sal_Int32 nAttrs, ...)
 {
     if (!pAttrList.is())
         pAttrList = FastSerializerHelper::createAttrList();
@@ -337,7 +316,7 @@ static void 
AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAt
     va_end(args);
 }
 
-static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& 
pAttrList, sal_Int32 nAttrName, const char* sAttrValue)
+void 
DocxAttributeOutput::AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>&
 pAttrList, sal_Int32 nAttrName, const char* sAttrValue)
 {
     AddToAttrList(pAttrList, 1, nAttrName, sAttrValue);
 }
@@ -542,7 +521,7 @@ sal_Int32 
DocxAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t p
     return nParaId;
 }
 
-static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
+OString DocxAttributeOutput::convertToOOXMLVertOrient(sal_Int16 nOrient)
 {
     switch( nOrient )
     {
@@ -562,7 +541,7 @@ static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
     }
 }
 
-static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle)
+OString DocxAttributeOutput::convertToOOXMLHoriOrient(sal_Int16 nOrient, bool 
bIsPosToggle)
 {
     switch( nOrient )
     {
@@ -582,7 +561,7 @@ static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, 
bool bIsPosToggle)
     }
 }
 
-static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
+OString DocxAttributeOutput::convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
 {
     switch (nOrientRel)
     {
@@ -597,7 +576,7 @@ static OString convertToOOXMLVertOrientRel(sal_Int16 
nOrientRel)
     }
 }
 
-static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)
+OString DocxAttributeOutput::convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)
 {
     switch (nOrientRel)
     {
@@ -644,7 +623,7 @@ void SdtBlockHelper::WriteSdtBlock(const 
::sax_fastparser::FSHelperPtr& pSeriali
         return;
 
     // sdt start mark
-    pSerializer->mark(Tag_WriteSdtBlock);
+    pSerializer->mark(DocxAttributeOutput::Tag_WriteSdtBlock);
 
     pSerializer->startElementNS(XML_w, XML_sdt);
 
@@ -688,7 +667,7 @@ void SdtBlockHelper::WriteSdtBlock(const 
::sax_fastparser::FSHelperPtr& pSeriali
     pSerializer->startElementNS(XML_w, XML_sdtContent);
 
     // prepend the tags since the sdt start mark before the paragraph
-    pSerializer->mergeTopMarks(Tag_WriteSdtBlock, 
sax_fastparser::MergeMarks::PREPEND);
+    pSerializer->mergeTopMarks(DocxAttributeOutput::Tag_WriteSdtBlock, 
sax_fastparser::MergeMarks::PREPEND);
 
     // write the ending tags after the paragraph
     m_bStartedSdt = true;
@@ -771,15 +750,15 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const 
uno::Sequence<beans::Property
             {
                 OUString sValue = rProp.Value.get<OUString>();
                 if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
-                    AddToAttrList(m_pTokenChildren,
+                    DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
                         FSNS(XML_w14, XML_checked),
                         OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
                 else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState")
-                    AddToAttrList(m_pTokenChildren,
+                    DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
                         FSNS(XML_w14, XML_checkedState),
                         OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
                 else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState")
-                    AddToAttrList(m_pTokenChildren,
+                    DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
                         FSNS(XML_w14, XML_uncheckedState),
                         OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
             }
@@ -792,15 +771,15 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const 
uno::Sequence<beans::Property
             {
                 OUString sValue = rProp.Value.get<OUString>();
                 if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings")
-                    AddToAttrList( m_pDataBindingAttrs,
+                    DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
                                     FSNS( XML_w, XML_prefixMappings ),
                                     OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
                 else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
-                    AddToAttrList( m_pDataBindingAttrs,
+                    DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
                                     FSNS( XML_w, XML_xpath ),
                                     OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
                 else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID")
-                    AddToAttrList( m_pDataBindingAttrs,
+                    DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
                                     FSNS( XML_w, XML_storeItemID ),
                                     OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
             }
@@ -815,7 +794,7 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const 
uno::Sequence<beans::Property
                 {
                     OUString sValue = rProp.Value.get<OUString>();
                     if (rProp.Name == "ooxml:CT_SdtText_multiLine")
-                        AddToAttrList(m_pTextAttrs,
+                        DocxAttributeOutput::AddToAttrList(m_pTextAttrs,
                             FSNS(XML_w, XML_multiLine),
                             OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
                 }
@@ -899,18 +878,18 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const 
uno::Sequence<beans::Property
             {
                 OUString sValue = rProp.Value.get<OUString>();
                 if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery")
-                    AddToAttrList(m_pTokenChildren,
+                    DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
                         FSNS(XML_w, XML_docPartGallery),
                         OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
                 else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory")
-                    AddToAttrList(m_pTokenChildren,
+                    DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
                         FSNS(XML_w, XML_docPartCategory),
                         OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
                 else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique")
                 {
                     if (sValue.isEmpty())
                         sValue = "true";
-                    AddToAttrList(m_pTokenChildren, FSNS(XML_w, 
XML_docPartUnique),
+                    DocxAttributeOutput::AddToAttrList(m_pTokenChildren, 
FSNS(XML_w, XML_docPartUnique),
                         OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
                 }
             }
@@ -4266,7 +4245,7 @@ static void impl_borders( FSHelperPtr const & pSerializer,
     }
 }
 
-static void impl_cellMargins( FSHelperPtr const & pSerializer, const 
SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* 
pDefaultMargins = nullptr)
+void DocxAttributeOutput::ImplCellMargins( FSHelperPtr const & pSerializer, 
const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* 
pDefaultMargins)
 {
     static const SvxBoxItemLine aBorders[] =
     {
@@ -4379,7 +4358,7 @@ void DocxAttributeOutput::TableCellProperties( 
ww8::WW8TableNodeInfoInner::Point
 
     {
         // Cell margins
-        impl_cellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox 
);
+        DocxAttributeOutput::ImplCellMargins( m_pSerializer, rBox, XML_tcMar, 
!bEcma, &rDefaultBox );
     }
 
     TableVerticalCell( pTableTextNodeInfoInner );
@@ -4517,787 +4496,6 @@ void DocxAttributeOutput::EndTableCell(sal_uInt32 nCell)
     m_tableReference->m_bTableCellParaSdtOpen = false;
 }
 
-void DocxAttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t 
/*pTableTextNodeInfoInner*/ )
-{
-}
-
-void DocxAttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t 
/*pTableTextNodeInfo*/ )
-{
-}
-
-namespace
-{
-
-/// Does the same as comphelper::string::padToLength(), but extends the start, 
not the end.
-OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, char 
cFill)
-{
-    if (nLen > aString.getLength())
-    {
-        sal_Int32 nDiff = nLen - aString.getLength();
-        OStringBuffer aBuffer;
-        comphelper::string::padToLength(aBuffer, nDiff, cFill);
-        aBuffer.append(aString);
-        return aBuffer.makeStringAndClear();
-    }
-    else
-        return aString;
-}
-
-//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
-//Since this is not import code, "-1" needs to be handled as the mode that LO 
will save as.
-//To identify how your code should handle a "-1", look in 
DocxExport::WriteSettings().
-sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport)
-{
-    sal_Int32 nWordCompatibilityMode = 
rDocExport.getWordCompatibilityModeFromGrabBag();
-
-    // TODO: this is duplicated, better store it in DocxExport member?
-    if 
(!rDocExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
-    {
-        if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
-        {
-            nWordCompatibilityMode = 14;
-        }
-    }
-
-    return nWordCompatibilityMode;
-}
-
-void CollectFloatingTableAttributes(DocxExport& rExport, const ww8::Frame& 
rFrame,
-                                    ww8::WW8TableNodeInfoInner::Pointer_t 
pTableTextNodeInfoInner,
-                                    rtl::Reference<FastAttributeList>& 
pAttributes)
-{
-    // we export the values of the surrounding Frame
-    OString sOrientation;
-    sal_Int32 nValue;
-
-    // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY!
-    OString sTblpXSpec
-        = 
convertToOOXMLHoriOrient(rFrame.GetFrameFormat().GetHoriOrient().GetHoriOrient(),
-                                   
rFrame.GetFrameFormat().GetHoriOrient().IsPosToggle());
-    OString sTblpYSpec
-        = 
convertToOOXMLVertOrient(rFrame.GetFrameFormat().GetVertOrient().GetVertOrient());
-
-    sOrientation
-        = 
convertToOOXMLVertOrientRel(rFrame.GetFrameFormat().GetVertOrient().GetRelationOrient());
-    pAttributes->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
-
-    if (!sTblpYSpec.isEmpty())
-        pAttributes->add(FSNS(XML_w, XML_tblpYSpec), sTblpYSpec);
-
-    sOrientation
-        = 
convertToOOXMLHoriOrientRel(rFrame.GetFrameFormat().GetHoriOrient().GetRelationOrient());
-    pAttributes->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
-
-    if (!sTblpXSpec.isEmpty())
-        pAttributes->add(FSNS(XML_w, XML_tblpXSpec), sTblpXSpec);
-
-    nValue = rFrame.GetFrameFormat().GetULSpace().GetLower();
-    if (nValue != 0)
-        pAttributes->add(FSNS(XML_w, XML_bottomFromText), 
OString::number(nValue));
-
-    nValue = rFrame.GetFrameFormat().GetLRSpace().GetLeft();
-    if (nValue != 0)
-        pAttributes->add(FSNS(XML_w, XML_leftFromText), 
OString::number(nValue));
-
-    nValue = rFrame.GetFrameFormat().GetLRSpace().GetRight();
-    if (nValue != 0)
-        pAttributes->add(FSNS(XML_w, XML_rightFromText), 
OString::number(nValue));
-
-    nValue = rFrame.GetFrameFormat().GetULSpace().GetUpper();
-    if (nValue != 0)
-        pAttributes->add(FSNS(XML_w, XML_topFromText), 
OString::number(nValue));
-
-    if (sTblpXSpec.isEmpty()) // do not write tblpX if tblpXSpec is present
-    {
-        nValue = rFrame.GetFrameFormat().GetHoriOrient().GetPos();
-        // we need to revert the additional shift introduced by
-        // lcl_DecrementHoriOrientPosition() in writerfilter
-        // 1st: left distance of the table
-        const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
-        const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
-        const SvxBoxItem& rBox = pFrameFormat->GetBox();
-        sal_Int32 nMode = lcl_getWordCompatibilityMode(rExport);
-        if (nMode < 15)
-        {
-            sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
-            nValue += nLeftDistance;
-        }
-
-        // 2nd: if a left border is given, revert the shift by half the width
-        // from lcl_DecrementHoriOrientPosition() in writerfilter
-        if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft())
-        {
-            tools::Long nWidth = pLeftBorder->GetWidth();
-            nValue += (nWidth / 2);
-        }
-
-        pAttributes->add(FSNS(XML_w, XML_tblpX), OString::number(nValue));
-    }
-
-    if (sTblpYSpec.isEmpty()) // do not write tblpY if tblpYSpec is present
-    {
-        nValue = rFrame.GetFrameFormat().GetVertOrient().GetPos();
-        pAttributes->add(FSNS(XML_w, XML_tblpY), OString::number(nValue));
-    }
-}
-}
-
-void DocxAttributeOutput::TableDefinition( 
ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
-    bool bEcma = GetExport().GetFilter().getVersion( ) == 
oox::core::ECMA_DIALECT;
-
-    // Write the table properties
-    m_pSerializer->startElementNS(XML_w, XML_tblPr);
-
-    static const sal_Int32 aOrder[] =
-    {
-        FSNS( XML_w, XML_tblStyle ),
-        FSNS( XML_w, XML_tblpPr ),
-        FSNS( XML_w, XML_tblOverlap ),
-        FSNS( XML_w, XML_bidiVisual ),
-        FSNS( XML_w, XML_tblStyleRowBandSize ),
-        FSNS( XML_w, XML_tblStyleColBandSize ),
-        FSNS( XML_w, XML_tblW ),
-        FSNS( XML_w, XML_jc ),
-        FSNS( XML_w, XML_tblCellSpacing ),
-        FSNS( XML_w, XML_tblInd ),
-        FSNS( XML_w, XML_tblBorders ),
-        FSNS( XML_w, XML_shd ),
-        FSNS( XML_w, XML_tblLayout ),
-        FSNS( XML_w, XML_tblCellMar ),
-        FSNS( XML_w, XML_tblLook ),
-        FSNS( XML_w, XML_tblPrChange )
-    };
-
-    // postpone the output so that we can later []
-    // prepend the properties before the run
-    // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with 
css::uno::Sequence
-    m_pSerializer->mark(Tag_TableDefinition, 
comphelper::containerToSequence(aOrder));
-
-    tools::Long nPageSize = 0;
-    const char* widthType = "dxa";
-
-    // If actual width of table is relative it should export is as "pct".`
-    const SwTable *pTable = pTableTextNodeInfoInner->getTable();
-    SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
-    const SwFormatFrameSize &rSize = pTableFormat->GetFrameSize();
-    int nWidthPercent = rSize.GetWidthPercent();
-    // If we export a floating table: we use the widthPercent of the 
surrounding frame
-    const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame();
-    if (pFloatingTableFrame)
-    {
-        const SwFormatFrameSize &rFrameSize = 
pFloatingTableFrame->GetFrameFormat().GetFrameSize();
-        nWidthPercent = rFrameSize.GetWidthPercent();
-    }
-
-    uno::Reference<beans::XPropertySet> 
xPropertySet(SwXTextTables::GetObject(*pTable->GetFrameFormat( 
)),uno::UNO_QUERY);
-    bool isWidthRelative = false;
-    xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
-    if (!isWidthRelative && !nWidthPercent)
-    {
-        // The best fit for "automatic" table placement is relative 100%
-        short nHoriOrient = -1;
-        xPropertySet->getPropertyValue("HoriOrient") >>= nHoriOrient;
-        isWidthRelative = nHoriOrient == text::HoriOrientation::FULL;
-        if (isWidthRelative)
-            nWidthPercent = 100;
-    }
-
-    if(isWidthRelative)
-    {
-       /**
-       * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - 
Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table 
Width Units)]
-       * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html
-       *
-       * Fiftieths of a Percent :
-       * 
http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
-       * pct Width is in Fiftieths of a Percent
-       *
-       * ex. If the Table width is 50% then
-       * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct
-       **/
-        nPageSize = nWidthPercent * 50 ;
-        widthType = "pct" ;
-    }
-    else
-    {
-        bool bRelBoxSize = false;
-        // Create the SwWriteTable instance to use col spans (and maybe other 
infos)
-        GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, 
bRelBoxSize );
-        if(nPageSize == 0)
-            widthType = "auto";
-    }
-
-    // Output the table preferred width
-    m_pSerializer->singleElementNS( XML_w, XML_tblW,
-            FSNS( XML_w, XML_w ), OString::number(nPageSize),
-            FSNS( XML_w, XML_type ), widthType );
-
-    // Disable layout autofit, as it does not exist in LibreOffice yet
-    m_pSerializer->singleElementNS( XML_w, XML_tblLayout,
-            FSNS( XML_w, XML_type ), "fixed" );
-
-    // Look for the table style property in the table grab bag
-    std::map<OUString, css::uno::Any> aGrabBag =
-            
pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
-
-    // We should clear the TableStyle map. In case of Table inside multiple 
tables it contains the
-    // table border style of the previous table.
-    std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = 
m_aTableStyleConfs.back();
-    rTableStyleConf.clear();
-
-    bool bFloatingTableWritten = false;
-    if (pFloatingTableFrame && 
pFloatingTableFrame->GetFrameFormat().GetFlySplit().GetValue())
-    {
-        rtl::Reference<FastAttributeList> pAttributes = 
FastSerializerHelper::createAttrList();
-        CollectFloatingTableAttributes(m_rExport, *pFloatingTableFrame, 
pTableTextNodeInfoInner,
-                                       pAttributes);
-        m_pSerializer->singleElementNS(XML_w, XML_tblpPr, pAttributes);
-        bFloatingTableWritten = true;
-    }
-
-    // Extract properties from grab bag
-    for( const auto & rGrabBagElement : aGrabBag )
-    {
-        if( rGrabBagElement.first == "TableStyleName")
-        {
-            OString sStyleName = OUStringToOString( 
rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
-            m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, 
XML_val), sStyleName);
-        }
-        else if( rGrabBagElement.first == "TableStyleTopBorder" )
-            rTableStyleConf[SvxBoxItemLine::TOP] = 
rGrabBagElement.second.get<table::BorderLine2>();
-        else if( rGrabBagElement.first == "TableStyleBottomBorder" )
-            rTableStyleConf[SvxBoxItemLine::BOTTOM]
-                = rGrabBagElement.second.get<table::BorderLine2>();
-        else if( rGrabBagElement.first == "TableStyleLeftBorder" )
-            rTableStyleConf[SvxBoxItemLine::LEFT]
-                = rGrabBagElement.second.get<table::BorderLine2>();
-        else if( rGrabBagElement.first == "TableStyleRightBorder" )
-            rTableStyleConf[SvxBoxItemLine::RIGHT]
-                = rGrabBagElement.second.get<table::BorderLine2>();
-        else if (rGrabBagElement.first == "TableStyleLook")
-        {
-            rtl::Reference<FastAttributeList> pAttributeList = 
FastSerializerHelper::createAttrList();
-            const uno::Sequence<beans::PropertyValue> aAttributeList = 
rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
-
-            for (const auto& rAttribute : aAttributeList)
-            {
-                if (rAttribute.Name == "val")
-                    pAttributeList->add(FSNS(XML_w, XML_val), 
lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16), 4, 
'0'));
-                else
-                {
-                    static DocxStringTokenMap const aTokens[] =
-                    {
-                        {"firstRow", XML_firstRow},
-                        {"lastRow", XML_lastRow},
-                        {"firstColumn", XML_firstColumn},
-                        {"lastColumn", XML_lastColumn},
-                        {"noHBand", XML_noHBand},
-                        {"noVBand", XML_noVBand},
-                        {nullptr, 0}
-                    };
-
-                    if (sal_Int32 nToken = DocxStringGetToken(aTokens, 
rAttribute.Name))
-                        pAttributeList->add(FSNS(XML_w, nToken), 
(rAttribute.Value.get<sal_Int32>() ? "1" : "0"));
-                }
-            }
-
-            m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList);
-        }
-        else if (rGrabBagElement.first == "TablePosition" &&
-                        // skip empty table position (tables in footnotes 
converted to
-                        // floating tables temporarily, don't export this)
-                        rGrabBagElement.second != uno::Any() )
-        {
-            rtl::Reference<FastAttributeList> attrListTablePos = 
FastSerializerHelper::createAttrList( );
-            const uno::Sequence<beans::PropertyValue> aTablePosition = 
rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >();
-            // look for a surrounding frame and take it's position values
-            const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
-            if( pFrame )
-            {
-                CollectFloatingTableAttributes(m_rExport, *pFrame, 
pTableTextNodeInfoInner,
-                                               attrListTablePos);
-            }
-            else // ( pFrame = 0 )
-            {
-                // we export the values from the grabBag
-                for (const auto& rProp : aTablePosition)
-                {
-                    if (rProp.Name == "vertAnchor" && 
!rProp.Value.get<OUString>().isEmpty())
-                    {
-                        OString sOrientation = OUStringToOString( 
rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
-                        attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), 
sOrientation);
-                    }
-                    else if (rProp.Name == "tblpYSpec" && 
!rProp.Value.get<OUString>().isEmpty())
-                    {
-                        OString sOrientation = OUStringToOString( 
rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
-                        attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), 
sOrientation);
-                    }
-                    else if (rProp.Name == "horzAnchor" && 
!rProp.Value.get<OUString>().isEmpty())
-                    {
-                        OString sOrientation = OUStringToOString( 
rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
-                        attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), 
sOrientation);
-                    }
-                    else if (rProp.Name == "tblpXSpec" && 
!rProp.Value.get<OUString>().isEmpty())
-                    {
-                        OString sOrientation = OUStringToOString( 
rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
-                        attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), 
sOrientation);
-                    }
-                    else if (rProp.Name == "bottomFromText")
-                    {
-                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
-                        attrListTablePos->add( FSNS( XML_w, XML_bottomFromText 
), OString::number( nValue ) );
-                    }
-                    else if (rProp.Name == "leftFromText")
-                    {
-                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
-                        attrListTablePos->add( FSNS( XML_w, XML_leftFromText 
), OString::number( nValue ) );
-                    }
-                    else if (rProp.Name == "rightFromText")
-                    {
-                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
-                        attrListTablePos->add( FSNS( XML_w, XML_rightFromText 
), OString::number( nValue ) );
-                    }
-                    else if (rProp.Name == "topFromText")
-                    {
-                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
-                        attrListTablePos->add( FSNS( XML_w, XML_topFromText ), 
OString::number( nValue ) );
-                    }
-                    else if (rProp.Name == "tblpX")
-                    {
-                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
-                        attrListTablePos->add( FSNS( XML_w, XML_tblpX ), 
OString::number( nValue ) );
-                    }
-                    else if (rProp.Name == "tblpY")
-                    {
-                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
-                        attrListTablePos->add( FSNS( XML_w, XML_tblpY ), 
OString::number( nValue ) );
-                    }
-                }
-            }
-
-            if (!bFloatingTableWritten)
-            {
-                m_pSerializer->singleElementNS(XML_w, XML_tblpPr, 
attrListTablePos);
-            }
-            attrListTablePos = nullptr;
-        }
-        else
-            SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: 
unhandled property: " << rGrabBagElement.first);
-    }
-
-    // Output the table alignment
-    const char* pJcVal;
-    sal_Int32 nIndent = 0;
-    switch ( pTableFormat->GetHoriOrient( ).GetHoriOrient( ) )
-    {
-        case text::HoriOrientation::CENTER:
-            pJcVal = "center";
-            break;
-        case text::HoriOrientation::RIGHT:
-            if ( bEcma )
-                pJcVal = "right";
-            else
-                pJcVal = "end";
-            break;
-        default:
-        case text::HoriOrientation::NONE:
-        case text::HoriOrientation::LEFT_AND_WIDTH:
-        {
-            if ( bEcma )
-                pJcVal = "left";
-            else
-                pJcVal = "start";
-            nIndent = sal_Int32( pTableFormat->GetLRSpace().GetLeft() );
-
-            // Table indentation has different meaning in Word, depending if 
the table is nested or not.
-            // If nested, tblInd is added to parent table's left spacing and 
defines left edge position
-            // If not nested, text position of left-most cell must be at 
absolute X = tblInd
-            // so, table_spacing + table_spacing_to_content = tblInd
-
-            // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), 
top-level tables are handled the same as nested tables;
-            // if no compatibilityMode is defined (which now should only 
happen on a new export to .docx),
-            // LO uses a higher compatibility than 2010's 14.
-            sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport);
-
-            const SwFrameFormat* pFrameFormat = 
pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
-            if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth 
== 0)
-                nIndent += pFrameFormat->GetBox().GetDistance( 
SvxBoxItemLine::LEFT );
-            else
-            {
-                // adjust for SW considering table to start mid-border instead 
of nested/2013's left-side-of-border.
-                nIndent -= pFrameFormat->GetBox().CalcLineWidth( 
SvxBoxItemLine::LEFT ) / 2;
-            }
-
-            break;
-        }
-    }
-    m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), 
pJcVal);
-
-    // Output the table background color (although cell value still needs to 
be specified)
-    const SvxBrushItem *pColorProp = 
pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
-    Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
-    if ( aColor != COL_AUTO )
-    {
-        OString sColor = msfilter::util::ConvertColor( aColor );
-        m_pSerializer->singleElementNS( XML_w, XML_shd,
-                FSNS( XML_w, XML_fill ), sColor,
-                FSNS( XML_w, XML_val ), "clear" );
-    }
-
-    // Output the table borders
-    TableDefaultBorders( pTableTextNodeInfoInner );
-
-    // Output the default cell margins
-    TableDefaultCellMargins( pTableTextNodeInfoInner );
-
-    TableBidi( pTableTextNodeInfoInner );
-
-    // Table indent (need to get written even if == 0)
-    m_pSerializer->singleElementNS( XML_w, XML_tblInd,
-            FSNS( XML_w, XML_w ), OString::number(nIndent),
-            FSNS( XML_w, XML_type ), "dxa" );
-
-    // Merge the marks for the ordered elements
-    m_pSerializer->mergeTopMarks(Tag_TableDefinition);
-
-    m_pSerializer->endElementNS( XML_w, XML_tblPr );
-
-    // Write the table grid infos
-    m_pSerializer->startElementNS(XML_w, XML_tblGrid);
-    sal_Int32 nPrv = 0;
-    ww8::WidthsPtr pColumnWidths = GetColumnWidths( pTableTextNodeInfoInner );
-    for ( auto aColumnWidth : *pColumnWidths )
-    {
-        sal_Int32 nWidth  =  sal_Int32( aColumnWidth ) - nPrv;
-        m_pSerializer->singleElementNS( XML_w, XML_gridCol,
-               FSNS( XML_w, XML_w ), OString::number(nWidth) );
-        nPrv = sal_Int32( aColumnWidth );
-    }
-
-    m_pSerializer->endElementNS( XML_w, XML_tblGrid );
-}
-
-void DocxAttributeOutput::TableDefaultBorders( 
ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
-{
-    // Table defaults should only be created IF m_aTableStyleConf contents 
haven't come from a table style.
-    // Previously this function wrote out Cell A1 as the table default, 
causing problems with no benefit.
-}
-
-void DocxAttributeOutput::TableDefaultCellMargins( 
ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
-{
-    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
-    const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
-    const SvxBoxItem& rBox = pFrameFormat->GetBox( );
-    const bool bEcma = GetExport().GetFilter().getVersion( ) == 
oox::core::ECMA_DIALECT;
-
-    impl_cellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma);
-}
-
-void DocxAttributeOutput::TableBackgrounds( 
ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
-    const SwTable *pTable = pTableTextNodeInfoInner->getTable();
-    const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
-    const SwTableLine *pTableRow = pTableBox->GetUpper();
-    const SwFrameFormat *pFormat = pTableBox->GetFrameFormat( );
-
-    const SvxBrushItem *pColorProp = 
pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
-    Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
-
-    const SwFrameFormat *pRowFormat = pTableRow->GetFrameFormat( );
-    const SvxBrushItem *pRowColorProp = 
pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
-    if ( pRowColorProp && aColor == COL_AUTO)
-        aColor = pRowColorProp->GetColor();
-
-    const SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
-    const SvxBrushItem *pTableColorProp = 
pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
-    if ( pTableColorProp && aColor == COL_AUTO )
-        aColor = pTableColorProp->GetColor();
-
-    const OString sColor = msfilter::util::ConvertColor( aColor );
-
-    std::map<OUString, css::uno::Any> aGrabBag =
-            
pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
-
-    OString sOriginalColor;
-    std::map<OUString, css::uno::Any>::iterator aGrabBagElement = 
aGrabBag.find("originalColor");
-    if( aGrabBagElement != aGrabBag.end() )
-        sOriginalColor = OUStringToOString( 
aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
-
-    if ( sOriginalColor != sColor )
-    {
-        // color changed by the user, or no grab bag: write sColor
-        if ( sColor != "auto" )
-        {
-            m_pSerializer->singleElementNS( XML_w, XML_shd,
-                FSNS( XML_w, XML_fill ), sColor,
-                FSNS( XML_w, XML_val ), "clear" );
-        }
-    }
-    else
-    {
-        rtl::Reference<sax_fastparser::FastAttributeList> pAttrList;
-
-        for( const auto & rGrabBagElement : aGrabBag )
-        {
-            if (!rGrabBagElement.second.has<OUString>())
-                continue;
-
-            OString sValue = OUStringToOString( 
rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
-            if( rGrabBagElement.first == "themeFill")
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFill ), 
sValue.getStr() );
-            else if( rGrabBagElement.first == "themeFillTint")
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillTint ), 
sValue.getStr() );
-            else if( rGrabBagElement.first == "themeFillShade")
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillShade ), 
sValue.getStr() );
-            else if( rGrabBagElement.first == "fill" )
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_fill ), 
sValue.getStr() );
-            else if( rGrabBagElement.first == "themeColor")
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_themeColor ), 
sValue.getStr() );
-            else if( rGrabBagElement.first == "themeTint")
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_themeTint ), 
sValue.getStr() );
-            else if( rGrabBagElement.first == "themeShade")
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_themeShade ), 
sValue.getStr() );
-            else if( rGrabBagElement.first == "color")
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_color ), 
sValue.getStr() );
-            else if( rGrabBagElement.first == "val")
-                AddToAttrList( pAttrList, FSNS( XML_w, XML_val ), 
sValue.getStr() );
-        }
-        m_pSerializer->singleElementNS( XML_w, XML_shd, pAttrList.get() );
-    }
-}
-
-void DocxAttributeOutput::TableRowRedline( 
ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
-    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
-    const SwTableLine * pTabLine = pTabBox->GetUpper();
-
-    bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
-        SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
-
-    // check table row property "HasTextChangesOnly"
-    SwRedlineTable::size_type nPos(0);
-    SwRedlineTable::size_type nChange = pTabLine->UpdateTextChangesOnly(nPos);
-    if ( nChange != SwRedlineTable::npos )
-    {
-        const SwRedlineTable& aRedlineTable = 
m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
-        const SwRangeRedline* pRedline = aRedlineTable[ nChange ];
-        SwTableRowRedline* pTableRowRedline = nullptr;
-        bool bIsInExtra = false;
-
-        // use the original DOCX redline data stored in ExtraRedlineTable,
-        // if it exists and its type wasn't changed
-        const SwExtraRedlineTable& aExtraRedlineTable = 
m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
-        for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < 
aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
-        {
-            SwExtraRedline* pExtraRedline = 
aExtraRedlineTable.GetRedline(nCurRedlinePos);
-            pTableRowRedline = dynamic_cast<SwTableRowRedline*>(pExtraRedline);
-            if (pTableRowRedline && &pTableRowRedline->GetTableLine() == 
pTabLine)
-            {
-                bIsInExtra = true;
-                break;
-            }
-        }
-
-        const SwRedlineData& aRedlineData = bIsInExtra &&
-            // still the same type (an inserted row could become a tracked 
deleted one)
-            pTableRowRedline->GetRedlineData().GetType() == 
pRedline->GetRedlineData().GetType()
-                ? pTableRowRedline->GetRedlineData()
-                : pRedline->GetRedlineData();
-
-        // Note: all redline ranges and table row redline (with the same 
author and timestamp)
-        // use the same redline id in OOXML exported by MSO, but it seems, the 
recent solution
-        // (different IDs for different ranges, also row changes) is also 
portable.
-        OString aId( OString::number( m_nRedlineId++ ) );
-        const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( 
aRedlineData.GetAuthor() ) );
-        OString aAuthor( OUStringToOString( bRemovePersonalInfo
-                        ? "Author" + OUString::number( 
GetExport().GetInfoID(rAuthor) )
-                        : rAuthor, RTL_TEXTENCODING_UTF8 ) );
-
-        const DateTime aDateTime = aRedlineData.GetTimeStamp();
-        bool bNoDate = bRemovePersonalInfo ||
-            ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && 
aDateTime.GetDay() == 1 );
-
-        if ( bNoDate )
-            m_pSerializer->singleElementNS( XML_w,
-                            RedlineType::Delete == pRedline->GetType() ? 
XML_del : XML_ins,
-                            FSNS( XML_w, XML_id ), aId,
-                            FSNS( XML_w, XML_author ), aAuthor );
-        else
-            m_pSerializer->singleElementNS( XML_w,
-                            RedlineType::Delete == pRedline->GetType() ? 
XML_del : XML_ins,
-                            FSNS( XML_w, XML_id ), aId,
-                            FSNS( XML_w, XML_author ), aAuthor,
-                            FSNS( XML_w, XML_date ), DateTimeToOString( 
aDateTime ) );
-        return;
-    }
-}
-
-void DocxAttributeOutput::TableCellRedline( 
ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
-    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
-
-    bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
-        SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
-
-    // search next Redline
-    const SwExtraRedlineTable& aExtraRedlineTable = 
m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
-    for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < 
aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
-    {
-        SwExtraRedline* pExtraRedline = 
aExtraRedlineTable.GetRedline(nCurRedlinePos);
-        const SwTableCellRedline* pTableCellRedline = dynamic_cast<const 
SwTableCellRedline*>(pExtraRedline);
-        if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox)
-        {
-            // Redline for this table cell
-            const SwRedlineData& aRedlineData = 
pTableCellRedline->GetRedlineData();
-            RedlineType nRedlineType = aRedlineData.GetType();
-            switch (nRedlineType)
-            {
-                case RedlineType::TableCellInsert:
-                case RedlineType::TableCellDelete:
-                {
-                    OString aId( OString::number( m_nRedlineId++ ) );
-                    const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( 
aRedlineData.GetAuthor() ) );
-                    OString aAuthor( OUStringToOString( bRemovePersonalInfo
-                        ? "Author" + OUString::number( 
GetExport().GetInfoID(rAuthor) )
-                        : rAuthor, RTL_TEXTENCODING_UTF8 ) );
-
-                    sal_Int32 nElement = nRedlineType == 
RedlineType::TableCellInsert
-                        ? XML_cellIns
-                        : XML_cellDel;
-                    const DateTime aDateTime = aRedlineData.GetTimeStamp();
-                    bool bNoDate = bRemovePersonalInfo || ( 
aDateTime.GetYear() == 1970 &&
-                                        aDateTime.GetMonth() == 1 && 
aDateTime.GetDay() == 1 );
-                    if ( bNoDate )
-                        m_pSerializer->singleElementNS( XML_w, nElement,
-                            FSNS( XML_w, XML_id ), aId,
-                            FSNS( XML_w, XML_author ), aAuthor );
-                    else
-                        m_pSerializer->singleElementNS( XML_w, nElement,
-                            FSNS( XML_w, XML_id ), aId,
-                            FSNS( XML_w, XML_author ), aAuthor,
-                            FSNS( XML_w, XML_date ), DateTimeToOString( 
aDateTime ) );
-                }
-                break;
-                default: break;
-            }
-        }
-    }
-}
-
-void DocxAttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t 
pTableTextNodeInfoInner )
-{
-    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
-    const SwTableLine * pTabLine = pTabBox->GetUpper();
-    const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
-
-    const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
-    if ( !(SwFrameSize::Variable != rLSz.GetHeightSizeType() && 
rLSz.GetHeight()) )
-        return;
-
-    sal_Int32 nHeight = rLSz.GetHeight();
-    const char *pRule = nullptr;
-
-    switch ( rLSz.GetHeightSizeType() )
-    {
-        case SwFrameSize::Fixed: pRule = "exact"; break;
-        case SwFrameSize::Minimum: pRule = "atLeast"; break;
-        default:           break;
-    }
-
-    if ( pRule )
-        m_pSerializer->singleElementNS( XML_w, XML_trHeight,
-                FSNS( XML_w, XML_val ), OString::number(nHeight),
-                FSNS( XML_w, XML_hRule ), pRule );
-}
-
-void DocxAttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t 
pTableTextNodeInfoInner )
-{
-    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
-    const SwTableLine * pTabLine = pTabBox->GetUpper();
-    const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
-
-    const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit( );
-    // if rSplittable is true then no need to write <w:cantSplit 
w:val="false"/>
-    // as default row prop is allow row to break across page.
-    if( !rSplittable.GetValue( ) )
-        m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, 
XML_val), "true");
-}
-
-void DocxAttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t 
pTableTextNodeInfoInner )
-{
-    const SwTable * pTable = pTableTextNodeInfoInner->getTable();
-    const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
-
-    if ( m_rExport.TrueFrameDirection( *pFrameFormat ) == 
SvxFrameDirection::Horizontal_RL_TB )
-    {
-        m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, 
XML_val), "true");
-    }
-}
-
-void DocxAttributeOutput::TableVerticalCell( 
ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
-    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
-    const SwFrameFormat *pFrameFormat = pTabBox->GetFrameFormat( );
-
-    if ( SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection( 
*pFrameFormat ) )
-        m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, 
XML_val), "tbRl");
-    else if ( SvxFrameDirection::Vertical_LR_BT == 
m_rExport.TrueFrameDirection( *pFrameFormat ) )
-    {
-        m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, 
XML_val), "btLr");
-    }
-
-    const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
-    const auto nRow = pTableTextNodeInfoInner->getRow();
-    if (nRow >= rRows.size())
-    {
-        SAL_WARN("sw.ww8", "DocxAttributeOutput::TableCellProperties: out of 
range row: " << nRow);
-        return;
-    }
-    SwWriteTableRow *pRow = rRows[nRow].get();
-    sal_uInt32 nCell = pTableTextNodeInfoInner->getCell();
-    const SwWriteTableCells& rTableCells =  pRow->GetCells();
-    if (nCell >= rTableCells.size() )
-        return;
-
-    const SwWriteTableCell *const pCell = pRow->GetCells()[ nCell ].get();
-    switch( pCell->GetVertOri())
-    {
-    case text::VertOrientation::TOP:
-        break;
-    case text::VertOrientation::CENTER:
-        m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, 
XML_val), "center");
-        break;
-    case text::VertOrientation::BOTTOM:
-        m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, 
XML_val), "bottom");
-        break;
-    }
-}
-
-void DocxAttributeOutput::TableNodeInfoInner( 
ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner )
-{
-    // This is called when the nested table ends in a cell, and there's no
-    // paragraph behind that; so we must check for the ends of cell, rows,
-    // tables
-    // ['true' to write an empty paragraph, MS Word insists on that]
-    FinishTableRowCell( pNodeInfoInner, true );
-}
-
-void DocxAttributeOutput::TableOrientation( 
ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
-{
-    SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableOrientation( 
ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
-}
-
-void DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t 
/*pTableTextNodeInfoInner*/ )
-{
-    SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableSpacing( 
ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
-}
-
-void DocxAttributeOutput::TableRowEnd( sal_uInt32 /*nDepth*/ )
-{
-    SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 
nDepth = 1 )" );
-}
-
 void DocxAttributeOutput::StartStyles()
 {
     m_pSerializer->startElementNS( XML_w, XML_styles,
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx 
b/sw/source/filter/ww8/docxattributeoutput.hxx
index f61765d62832..bea87225ba50 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -1096,6 +1096,32 @@ public:
     void pushToTableExportContext(DocxTableExportContext& rContext);
     /// Restores from the remembered state.
     void popFromTableExportContext(DocxTableExportContext const & rContext);
+
+    static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool 
bIsPosToggle);
+    static OString convertToOOXMLVertOrient(sal_Int16 nOrient);
+    static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel);
+    static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel);
+    static void ImplCellMargins( sax_fastparser::FSHelperPtr const & 
pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const 
SvxBoxItem* pDefaultMargins = nullptr);
+    static void 
AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, 
sal_Int32 nAttrs, ...);
+    static void 
AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, 
sal_Int32 nAttrName, const char* sAttrValue);
+
+    static const sal_Int32 Tag_StartParagraph_1 = 1;
+    static const sal_Int32 Tag_StartParagraph_2 = 2;
+    static const sal_Int32 Tag_WriteSdtBlock = 3;
+    static const sal_Int32 Tag_StartParagraphProperties = 4;
+    static const sal_Int32 Tag_InitCollectedParagraphProperties = 5;
+    static const sal_Int32 Tag_StartRun_1 = 6;
+    static const sal_Int32 Tag_StartRun_2 = 7;
+    static const sal_Int32 Tag_StartRun_3 = 8;
+    static const sal_Int32 Tag_EndRun_1 = 9;
+    static const sal_Int32 Tag_EndRun_2 = 10;
+    static const sal_Int32 Tag_StartRunProperties = 11;
+    static const sal_Int32 Tag_InitCollectedRunProperties = 12;
+    static const sal_Int32 Tag_Redline_1 = 13;
+    static const sal_Int32 Tag_Redline_2 = 14;
+    static const sal_Int32 Tag_TableDefinition = 15;
+    static const sal_Int32 Tag_OutputFlyFrame = 16;
+    static const sal_Int32 Tag_StartSection = 17;
 };
 
 /**
diff --git a/sw/source/filter/ww8/docxtableexport.cxx 
b/sw/source/filter/ww8/docxtableexport.cxx
new file mode 100644
index 000000000000..2bed2576f738
--- /dev/null
+++ b/sw/source/filter/ww8/docxtableexport.cxx
@@ -0,0 +1,869 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "docxattributeoutput.hxx"
+
+#include <com/sun/star/text/XTextTable.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <svl/grabbagitem.hxx>
+#include <sax/fshelper.hxx>
+#include <editeng/ulspitem.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <tools/datetimeutils.hxx>
+
+#include <fmtfsize.hxx>
+#include <unocoll.hxx>
+#include <formatflysplit.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <frmatr.hxx>
+#include <swmodule.hxx>
+#include <fmtrowsplt.hxx>
+
+#include "docxexportfilter.hxx"
+#include "docxhelper.hxx"
+
+using namespace com::sun::star;
+using namespace sax_fastparser;
+using namespace oox;
+
+namespace
+{
+/// Does the same as comphelper::string::padToLength(), but extends the start, 
not the end.
+OString lcl_padStartToLength(OString const& aString, sal_Int32 nLen, char 
cFill)
+{
+    if (nLen > aString.getLength())
+    {
+        sal_Int32 nDiff = nLen - aString.getLength();
+        OStringBuffer aBuffer;
+        comphelper::string::padToLength(aBuffer, nDiff, cFill);
+        aBuffer.append(aString);
+        return aBuffer.makeStringAndClear();
+    }
+    else
+        return aString;
+}
+
+//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
+//Since this is not import code, "-1" needs to be handled as the mode that LO 
will save as.
+//To identify how your code should handle a "-1", look in 
DocxExport::WriteSettings().
+sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport)
+{
+    sal_Int32 nWordCompatibilityMode = 
rDocExport.getWordCompatibilityModeFromGrabBag();
+
+    // TODO: this is duplicated, better store it in DocxExport member?
+    if 
(!rDocExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
+    {
+        if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
+        {
+            nWordCompatibilityMode = 14;
+        }
+    }
+
+    return nWordCompatibilityMode;
+}
+
+void CollectFloatingTableAttributes(DocxExport& rExport, const ww8::Frame& 
rFrame,
+                                    ww8::WW8TableNodeInfoInner::Pointer_t 
pTableTextNodeInfoInner,
+                                    rtl::Reference<FastAttributeList>& 
pAttributes)
+{
+    // we export the values of the surrounding Frame
+    OString sOrientation;
+    sal_Int32 nValue;
+
+    // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY!
+    OString sTblpXSpec = DocxAttributeOutput::convertToOOXMLHoriOrient(
+        rFrame.GetFrameFormat().GetHoriOrient().GetHoriOrient(),
+        rFrame.GetFrameFormat().GetHoriOrient().IsPosToggle());
+    OString sTblpYSpec = DocxAttributeOutput::convertToOOXMLVertOrient(
+        rFrame.GetFrameFormat().GetVertOrient().GetVertOrient());
+
+    sOrientation = DocxAttributeOutput::convertToOOXMLVertOrientRel(
+        rFrame.GetFrameFormat().GetVertOrient().GetRelationOrient());
+    pAttributes->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
+
+    if (!sTblpYSpec.isEmpty())
+        pAttributes->add(FSNS(XML_w, XML_tblpYSpec), sTblpYSpec);
+
+    sOrientation = DocxAttributeOutput::convertToOOXMLHoriOrientRel(
+        rFrame.GetFrameFormat().GetHoriOrient().GetRelationOrient());
+    pAttributes->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
+
+    if (!sTblpXSpec.isEmpty())
+        pAttributes->add(FSNS(XML_w, XML_tblpXSpec), sTblpXSpec);
+
+    nValue = rFrame.GetFrameFormat().GetULSpace().GetLower();
+    if (nValue != 0)
+        pAttributes->add(FSNS(XML_w, XML_bottomFromText), 
OString::number(nValue));
+
+    nValue = rFrame.GetFrameFormat().GetLRSpace().GetLeft();
+    if (nValue != 0)
+        pAttributes->add(FSNS(XML_w, XML_leftFromText), 
OString::number(nValue));
+
+    nValue = rFrame.GetFrameFormat().GetLRSpace().GetRight();
+    if (nValue != 0)
+        pAttributes->add(FSNS(XML_w, XML_rightFromText), 
OString::number(nValue));
+
+    nValue = rFrame.GetFrameFormat().GetULSpace().GetUpper();
+    if (nValue != 0)
+        pAttributes->add(FSNS(XML_w, XML_topFromText), 
OString::number(nValue));
+
+    if (sTblpXSpec.isEmpty()) // do not write tblpX if tblpXSpec is present
+    {
+        nValue = rFrame.GetFrameFormat().GetHoriOrient().GetPos();
+        // we need to revert the additional shift introduced by
+        // lcl_DecrementHoriOrientPosition() in writerfilter
+        // 1st: left distance of the table
+        const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+        const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
+        const SvxBoxItem& rBox = pFrameFormat->GetBox();
+        sal_Int32 nMode = lcl_getWordCompatibilityMode(rExport);
+        if (nMode < 15)
+        {
+            sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
+            nValue += nLeftDistance;
+        }
+
+        // 2nd: if a left border is given, revert the shift by half the width
+        // from lcl_DecrementHoriOrientPosition() in writerfilter
+        if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft())
+        {
+            tools::Long nWidth = pLeftBorder->GetWidth();
+            nValue += (nWidth / 2);
+        }
+
+        pAttributes->add(FSNS(XML_w, XML_tblpX), OString::number(nValue));
+    }
+
+    if (sTblpYSpec.isEmpty()) // do not write tblpY if tblpYSpec is present
+    {
+        nValue = rFrame.GetFrameFormat().GetVertOrient().GetPos();
+        pAttributes->add(FSNS(XML_w, XML_tblpY), OString::number(nValue));
+    }
+}
+}
+
+void DocxAttributeOutput::TableInfoCell(
+    ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+}
+
+void DocxAttributeOutput::TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t 
/*pTableTextNodeInfo*/)
+{
+}
+
+void DocxAttributeOutput::TableDefinition(
+    ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+    bool const bEcma = GetExport().GetFilter().getVersion() == 
oox::core::ECMA_DIALECT;
+
+    // Write the table properties
+    m_pSerializer->startElementNS(XML_w, XML_tblPr);
+
+    static const sal_Int32 aOrder[] = { FSNS(XML_w, XML_tblStyle),
+                                        FSNS(XML_w, XML_tblpPr),
+                                        FSNS(XML_w, XML_tblOverlap),
+                                        FSNS(XML_w, XML_bidiVisual),
+                                        FSNS(XML_w, XML_tblStyleRowBandSize),
+                                        FSNS(XML_w, XML_tblStyleColBandSize),
+                                        FSNS(XML_w, XML_tblW),
+                                        FSNS(XML_w, XML_jc),
+                                        FSNS(XML_w, XML_tblCellSpacing),
+                                        FSNS(XML_w, XML_tblInd),
+                                        FSNS(XML_w, XML_tblBorders),
+                                        FSNS(XML_w, XML_shd),
+                                        FSNS(XML_w, XML_tblLayout),
+                                        FSNS(XML_w, XML_tblCellMar),
+                                        FSNS(XML_w, XML_tblLook),
+                                        FSNS(XML_w, XML_tblPrChange) };
+
+    // postpone the output so that we can later []
+    // prepend the properties before the run
+    // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with 
css::uno::Sequence
+    m_pSerializer->mark(Tag_TableDefinition, 
comphelper::containerToSequence(aOrder));
+
+    tools::Long nPageSize = 0;
+    const char* widthType = "dxa";
+
+    // If actual width of table is relative it should export is as "pct".`
+    const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+    SwFrameFormat* pTableFormat = pTable->GetFrameFormat();
+    const SwFormatFrameSize& rSize = pTableFormat->GetFrameSize();
+    int nWidthPercent = rSize.GetWidthPercent();
+    // If we export a floating table: we use the widthPercent of the 
surrounding frame
+    const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame();
+    if (pFloatingTableFrame)
+    {
+        const SwFormatFrameSize& rFrameSize = 
pFloatingTableFrame->GetFrameFormat().GetFrameSize();
+        nWidthPercent = rFrameSize.GetWidthPercent();
+    }
+
+    uno::Reference<beans::XPropertySet> xPropertySet(
+        SwXTextTables::GetObject(*pTable->GetFrameFormat()), uno::UNO_QUERY);
+    bool isWidthRelative = false;
+    xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
+    if (!isWidthRelative && !nWidthPercent)
+    {
+        // The best fit for "automatic" table placement is relative 100%
+        short nHoriOrient = -1;
+        xPropertySet->getPropertyValue("HoriOrient") >>= nHoriOrient;
+        isWidthRelative = nHoriOrient == text::HoriOrientation::FULL;
+        if (isWidthRelative)
+            nWidthPercent = 100;
+    }
+
+    if (isWidthRelative)
+    {
+        /**
+       * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - 
Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table 
Width Units)]
+       * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html
+       *
+       * Fiftieths of a Percent :
+       * 
http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
+       * pct Width is in Fiftieths of a Percent
+       *
+       * ex. If the Table width is 50% then
+       * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct
+       **/
+        nPageSize = nWidthPercent * 50;
+        widthType = "pct";
+    }
+    else
+    {
+        bool bRelBoxSize = false;
+        // Create the SwWriteTable instance to use col spans (and maybe other 
infos)
+        GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, 
bRelBoxSize);
+        if (nPageSize == 0)
+            widthType = "auto";
+    }
+
+    // Output the table preferred width
+    m_pSerializer->singleElementNS(XML_w, XML_tblW, FSNS(XML_w, XML_w), 
OString::number(nPageSize),
+                                   FSNS(XML_w, XML_type), widthType);
+
+    // Disable layout autofit, as it does not exist in LibreOffice yet
+    m_pSerializer->singleElementNS(XML_w, XML_tblLayout, FSNS(XML_w, 
XML_type), "fixed");
+
+    // Look for the table style property in the table grab bag
+    std::map<OUString, css::uno::Any> aGrabBag
+        = 
pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
+
+    // We should clear the TableStyle map. In case of Table inside multiple 
tables it contains the
+    // table border style of the previous table.
+    std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = 
m_aTableStyleConfs.back();
+    rTableStyleConf.clear();
+
+    bool bFloatingTableWritten = false;
+    if (pFloatingTableFrame && 
pFloatingTableFrame->GetFrameFormat().GetFlySplit().GetValue())
+    {
+        rtl::Reference<FastAttributeList> pAttributes = 
FastSerializerHelper::createAttrList();
+        CollectFloatingTableAttributes(m_rExport, *pFloatingTableFrame, 
pTableTextNodeInfoInner,
+                                       pAttributes);
+        m_pSerializer->singleElementNS(XML_w, XML_tblpPr, pAttributes);
+        bFloatingTableWritten = true;
+    }
+
+    // Extract properties from grab bag
+    for (const auto& rGrabBagElement : aGrabBag)
+    {
+        if (rGrabBagElement.first == "TableStyleName")
+        {
+            OString sStyleName
+                = OUStringToOString(rGrabBagElement.second.get<OUString>(), 
RTL_TEXTENCODING_UTF8);
+            m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, 
XML_val), sStyleName);
+        }
+        else if (rGrabBagElement.first == "TableStyleTopBorder")
+            rTableStyleConf[SvxBoxItemLine::TOP] = 
rGrabBagElement.second.get<table::BorderLine2>();
+        else if (rGrabBagElement.first == "TableStyleBottomBorder")
+            rTableStyleConf[SvxBoxItemLine::BOTTOM]
+                = rGrabBagElement.second.get<table::BorderLine2>();
+        else if (rGrabBagElement.first == "TableStyleLeftBorder")
+            rTableStyleConf[SvxBoxItemLine::LEFT]
+                = rGrabBagElement.second.get<table::BorderLine2>();
+        else if (rGrabBagElement.first == "TableStyleRightBorder")
+            rTableStyleConf[SvxBoxItemLine::RIGHT]
+                = rGrabBagElement.second.get<table::BorderLine2>();
+        else if (rGrabBagElement.first == "TableStyleLook")
+        {
+            rtl::Reference<FastAttributeList> pAttributeList
+                = FastSerializerHelper::createAttrList();
+            const uno::Sequence<beans::PropertyValue> aAttributeList
+                = 
rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue>>();
+
+            for (const auto& rAttribute : aAttributeList)
+            {
+                if (rAttribute.Name == "val")
+                    pAttributeList->add(
+                        FSNS(XML_w, XML_val),
+                        
lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16),
+                                             4, '0'));
+                else
+                {
+                    static DocxStringTokenMap const aTokens[]
+                        = { { "firstRow", XML_firstRow },
+                            { "lastRow", XML_lastRow },
+                            { "firstColumn", XML_firstColumn },
+                            { "lastColumn", XML_lastColumn },
+                            { "noHBand", XML_noHBand },
+                            { "noVBand", XML_noVBand },
+                            { nullptr, 0 } };
+
+                    if (sal_Int32 nToken = DocxStringGetToken(aTokens, 
rAttribute.Name))
+                        pAttributeList->add(FSNS(XML_w, nToken),
+                                            (rAttribute.Value.get<sal_Int32>() 
? "1" : "0"));
+                }
+            }
+
+            m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList);
+        }
+        else if (rGrabBagElement.first == "TablePosition" &&
+                 // skip empty table position (tables in footnotes converted to
+                 // floating tables temporarily, don't export this)
+                 rGrabBagElement.second != uno::Any())
+        {
+            rtl::Reference<FastAttributeList> attrListTablePos
+                = FastSerializerHelper::createAttrList();
+            const uno::Sequence<beans::PropertyValue> aTablePosition
+                = 
rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue>>();
+            // look for a surrounding frame and take it's position values
+            const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
+            if (pFrame)
+            {
+                CollectFloatingTableAttributes(m_rExport, *pFrame, 
pTableTextNodeInfoInner,
+                                               attrListTablePos);
+            }
+            else // ( pFrame = 0 )
+            {
+                // we export the values from the grabBag
+                for (const auto& rProp : aTablePosition)
+                {
+                    if (rProp.Name == "vertAnchor" && 
!rProp.Value.get<OUString>().isEmpty())
+                    {
+                        OString sOrientation
+                            = OUStringToOString(rProp.Value.get<OUString>(), 
RTL_TEXTENCODING_UTF8);
+                        attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), 
sOrientation);
+                    }
+                    else if (rProp.Name == "tblpYSpec" && 
!rProp.Value.get<OUString>().isEmpty())
+                    {
+                        OString sOrientation
+                            = OUStringToOString(rProp.Value.get<OUString>(), 
RTL_TEXTENCODING_UTF8);
+                        attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), 
sOrientation);
+                    }
+                    else if (rProp.Name == "horzAnchor" && 
!rProp.Value.get<OUString>().isEmpty())
+                    {
+                        OString sOrientation
+                            = OUStringToOString(rProp.Value.get<OUString>(), 
RTL_TEXTENCODING_UTF8);
+                        attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), 
sOrientation);
+                    }
+                    else if (rProp.Name == "tblpXSpec" && 
!rProp.Value.get<OUString>().isEmpty())
+                    {
+                        OString sOrientation
+                            = OUStringToOString(rProp.Value.get<OUString>(), 
RTL_TEXTENCODING_UTF8);
+                        attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), 
sOrientation);
+                    }
+                    else if (rProp.Name == "bottomFromText")
+                    {
+                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+                        attrListTablePos->add(FSNS(XML_w, XML_bottomFromText),
+                                              OString::number(nValue));
+                    }
+                    else if (rProp.Name == "leftFromText")
+                    {
+                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+                        attrListTablePos->add(FSNS(XML_w, XML_leftFromText),
+                                              OString::number(nValue));
+                    }
+                    else if (rProp.Name == "rightFromText")
+                    {
+                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+                        attrListTablePos->add(FSNS(XML_w, XML_rightFromText),
+                                              OString::number(nValue));
+                    }
+                    else if (rProp.Name == "topFromText")
+                    {
+                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+                        attrListTablePos->add(FSNS(XML_w, XML_topFromText),
+                                              OString::number(nValue));
+                    }
+                    else if (rProp.Name == "tblpX")
+                    {
+                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+                        attrListTablePos->add(FSNS(XML_w, XML_tblpX), 
OString::number(nValue));
+                    }
+                    else if (rProp.Name == "tblpY")
+                    {
+                        sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+                        attrListTablePos->add(FSNS(XML_w, XML_tblpY), 
OString::number(nValue));
+                    }
+                }
+            }
+
+            if (!bFloatingTableWritten)
+            {
+                m_pSerializer->singleElementNS(XML_w, XML_tblpPr, 
attrListTablePos);
+            }
+            attrListTablePos = nullptr;
+        }
+        else
+            SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: 
unhandled property: "
+                                   << rGrabBagElement.first);
+    }
+
+    // Output the table alignment
+    const char* pJcVal;
+    sal_Int32 nIndent = 0;
+    switch (pTableFormat->GetHoriOrient().GetHoriOrient())
+    {
+        case text::HoriOrientation::CENTER:
+            pJcVal = "center";
+            break;
+        case text::HoriOrientation::RIGHT:
+            if (bEcma)
+                pJcVal = "right";
+            else
+                pJcVal = "end";
+            break;
+        default:
+        case text::HoriOrientation::NONE:
+        case text::HoriOrientation::LEFT_AND_WIDTH:
+        {
+            if (bEcma)
+                pJcVal = "left";
+            else
+                pJcVal = "start";
+            nIndent = sal_Int32(pTableFormat->GetLRSpace().GetLeft());
+
+            // Table indentation has different meaning in Word, depending if 
the table is nested or not.
+            // If nested, tblInd is added to parent table's left spacing and 
defines left edge position
+            // If not nested, text position of left-most cell must be at 
absolute X = tblInd
+            // so, table_spacing + table_spacing_to_content = tblInd
+
+            // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), 
top-level tables are handled the same as nested tables;
+            // if no compatibilityMode is defined (which now should only 
happen on a new export to .docx),
+            // LO uses a higher compatibility than 2010's 14.
+            sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport);
+
+            const SwFrameFormat* pFrameFormat
+                = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
+            if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth 
== 0)
+                nIndent += 
pFrameFormat->GetBox().GetDistance(SvxBoxItemLine::LEFT);
+            else
+            {
+                // adjust for SW considering table to start mid-border instead 
of nested/2013's left-side-of-border.
+                nIndent -= 
pFrameFormat->GetBox().CalcLineWidth(SvxBoxItemLine::LEFT) / 2;
+            }
+
+            break;
+        }
+    }
+    m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), 
pJcVal);
+
+    // Output the table background color (although cell value still needs to 
be specified)
+    const SvxBrushItem* pColorProp
+        = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+    Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
+    if (aColor != COL_AUTO)
+    {
+        OString sColor = msfilter::util::ConvertColor(aColor);
+        m_pSerializer->singleElementNS(XML_w, XML_shd, FSNS(XML_w, XML_fill), 
sColor,
+                                       FSNS(XML_w, XML_val), "clear");
+    }
+
+    // Output the table borders
+    TableDefaultBorders(pTableTextNodeInfoInner);
+
+    // Output the default cell margins
+    TableDefaultCellMargins(pTableTextNodeInfoInner);
+
+    TableBidi(pTableTextNodeInfoInner);
+
+    // Table indent (need to get written even if == 0)
+    m_pSerializer->singleElementNS(XML_w, XML_tblInd, FSNS(XML_w, XML_w), 
OString::number(nIndent),
+                                   FSNS(XML_w, XML_type), "dxa");
+
+    // Merge the marks for the ordered elements
+    m_pSerializer->mergeTopMarks(Tag_TableDefinition);
+
+    m_pSerializer->endElementNS(XML_w, XML_tblPr);
+
+    // Write the table grid infos
+    m_pSerializer->startElementNS(XML_w, XML_tblGrid);
+    sal_Int32 nPrv = 0;
+    ww8::WidthsPtr pColumnWidths = GetColumnWidths(pTableTextNodeInfoInner);
+    for (auto aColumnWidth : *pColumnWidths)
+    {
+        sal_Int32 nWidth = sal_Int32(aColumnWidth) - nPrv;
+        m_pSerializer->singleElementNS(XML_w, XML_gridCol, FSNS(XML_w, XML_w),
+                                       OString::number(nWidth));
+        nPrv = sal_Int32(aColumnWidth);
+    }
+
+    m_pSerializer->endElementNS(XML_w, XML_tblGrid);
+}
+
+void DocxAttributeOutput::TableDefaultBorders(
+    ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+    // Table defaults should only be created IF m_aTableStyleConf contents 
haven't come from a table style.
+    // Previously this function wrote out Cell A1 as the table default, 
causing problems with no benefit.
+}
+
+void DocxAttributeOutput::TableDefaultCellMargins(
+    ww8::WW8TableNodeInfoInner::Pointer_t const& pTableTextNodeInfoInner)
+{
+    const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+    const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
+    const SvxBoxItem& rBox = pFrameFormat->GetBox();
+    const bool bEcma = GetExport().GetFilter().getVersion() == 
oox::core::ECMA_DIALECT;
+
+    DocxAttributeOutput::ImplCellMargins(m_pSerializer, rBox, XML_tblCellMar, 
!bEcma);
+}
+
+void DocxAttributeOutput::TableBackgrounds(
+    ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+    const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+    const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
+    const SwTableLine* pTableRow = pTableBox->GetUpper();
+    const SwFrameFormat* pFormat = pTableBox->GetFrameFormat();
+
+    const SvxBrushItem* pColorProp = 
pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+    Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
+
+    const SwFrameFormat* pRowFormat = pTableRow->GetFrameFormat();
+    const SvxBrushItem* pRowColorProp
+        = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+    if (pRowColorProp && aColor == COL_AUTO)
+        aColor = pRowColorProp->GetColor();
+
+    const SwFrameFormat* pTableFormat = pTable->GetFrameFormat();
+    const SvxBrushItem* pTableColorProp
+        = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+    if (pTableColorProp && aColor == COL_AUTO)
+        aColor = pTableColorProp->GetColor();
+
+    const OString sColor = msfilter::util::ConvertColor(aColor);
+
+    std::map<OUString, css::uno::Any> aGrabBag
+        = 
pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
+
+    OString sOriginalColor;
+    std::map<OUString, css::uno::Any>::iterator aGrabBagElement = 
aGrabBag.find("originalColor");
+    if (aGrabBagElement != aGrabBag.end())
+        sOriginalColor
+            = OUStringToOString(aGrabBagElement->second.get<OUString>(), 
RTL_TEXTENCODING_UTF8);
+
+    if (sOriginalColor != sColor)
+    {
+        // color changed by the user, or no grab bag: write sColor
+        if (sColor != "auto")
+        {
+            m_pSerializer->singleElementNS(XML_w, XML_shd, FSNS(XML_w, 
XML_fill), sColor,
+                                           FSNS(XML_w, XML_val), "clear");
+        }
+    }
+    else
+    {
+        rtl::Reference<sax_fastparser::FastAttributeList> pAttrList;
+
+        for (const auto& rGrabBagElement : aGrabBag)
+        {
+            if (!rGrabBagElement.second.has<OUString>())
+                continue;
+
+            OString sValue
+                = OUStringToOString(rGrabBagElement.second.get<OUString>(), 
RTL_TEXTENCODING_UTF8);
+            if (rGrabBagElement.first == "themeFill")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFill), 
sValue.getStr());
+            else if (rGrabBagElement.first == "themeFillTint")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFillTint), 
sValue.getStr());
+            else if (rGrabBagElement.first == "themeFillShade")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFillShade), 
sValue.getStr());
+            else if (rGrabBagElement.first == "fill")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_fill), 
sValue.getStr());
+            else if (rGrabBagElement.first == "themeColor")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_themeColor), 
sValue.getStr());
+            else if (rGrabBagElement.first == "themeTint")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_themeTint), 
sValue.getStr());
+            else if (rGrabBagElement.first == "themeShade")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_themeShade), 
sValue.getStr());
+            else if (rGrabBagElement.first == "color")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_color), 
sValue.getStr());
+            else if (rGrabBagElement.first == "val")
+                AddToAttrList(pAttrList, FSNS(XML_w, XML_val), 
sValue.getStr());
+        }
+        m_pSerializer->singleElementNS(XML_w, XML_shd, pAttrList.get());
+    }
+}
+
+void DocxAttributeOutput::TableRowRedline(
+    ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+    const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+    const SwTableLine* pTabLine = pTabBox->GetUpper();
+
+    bool bRemovePersonalInfo
+        = 
SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo);
+
+    // check table row property "HasTextChangesOnly"
+    SwRedlineTable::size_type nPos(0);
+    SwRedlineTable::size_type nChange = pTabLine->UpdateTextChangesOnly(nPos);
+    if (nChange != SwRedlineTable::npos)
+    {
+        const SwRedlineTable& aRedlineTable
+            = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+        const SwRangeRedline* pRedline = aRedlineTable[nChange];
+        SwTableRowRedline* pTableRowRedline = nullptr;
+        bool bIsInExtra = false;
+
+        // use the original DOCX redline data stored in ExtraRedlineTable,
+        // if it exists and its type wasn't changed
+        const SwExtraRedlineTable& aExtraRedlineTable
+            = 
m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
+        for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < 
aExtraRedlineTable.GetSize();
+             ++nCurRedlinePos)
+        {
+            SwExtraRedline* pExtraRedline = 
aExtraRedlineTable.GetRedline(nCurRedlinePos);
+            pTableRowRedline = dynamic_cast<SwTableRowRedline*>(pExtraRedline);
+            if (pTableRowRedline && &pTableRowRedline->GetTableLine() == 
pTabLine)
+            {
+                bIsInExtra = true;
+                break;
+            }
+        }
+
+        const SwRedlineData& aRedlineData
+            = bIsInExtra &&
+                      // still the same type (an inserted row could become a 
tracked deleted one)
+                      pTableRowRedline->GetRedlineData().GetType()
+                          == pRedline->GetRedlineData().GetType()
+                  ? pTableRowRedline->GetRedlineData()
+                  : pRedline->GetRedlineData();
+
+        // Note: all redline ranges and table row redline (with the same 
author and timestamp)
+        // use the same redline id in OOXML exported by MSO, but it seems, the 
recent solution
+        // (different IDs for different ranges, also row changes) is also 
portable.
+        OString aId(OString::number(m_nRedlineId++));
+        const OUString& 
rAuthor(SW_MOD()->GetRedlineAuthor(aRedlineData.GetAuthor()));
+        OString aAuthor(OUStringToOString(
+            bRemovePersonalInfo ? "Author" + 
OUString::number(GetExport().GetInfoID(rAuthor))
+                                : rAuthor,
+            RTL_TEXTENCODING_UTF8));
+
+        const DateTime aDateTime = aRedlineData.GetTimeStamp();
+        bool bNoDate = bRemovePersonalInfo
+                       || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth() 
== 1
+                           && aDateTime.GetDay() == 1);
+
+        if (bNoDate)
+            m_pSerializer->singleElementNS(
+                XML_w, RedlineType::Delete == pRedline->GetType() ? XML_del : 
XML_ins,

... etc. - the rest is truncated

Reply via email to