sw/qa/filter/ww8/ww8.cxx                                 |   39 +++
 sw/source/filter/ww8/docxattributeoutput.cxx             |  171 ++++++++-------
 writerfilter/source/dmapper/DomainMapperTableHandler.cxx |   28 +-
 3 files changed, 162 insertions(+), 76 deletions(-)

New commits:
commit e7be3b821cd42fdc9d8e51772b8202030d76497e
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Feb 22 08:11:00 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Feb 22 09:36:50 2023 +0000

    sw floatable: teach the DOCX filter about SwFormatFlySplit
    
    - stop creating a grab-bag for floating tables in the DOCX import if
      split flys are allowed, which gives the exporter an opportunity to
      actually read the doc model
    
    - extract that code that writes a <w:tblpPr> from a ww8::Frame to a new
      CollectFloatingTableAttributes()
    
    - in case a fly frame has a table and the fly has SwFormatFlySplit=true,
      then call CollectFloatingTableAttributes() even without grab-bags
    
    - in the unlikely case that we would have both a split fly and a
      grab-bag, ignore the grab-bag
    
    With this, we get a working DOCX export for multi-page floating tables.
    The import is still disabled by default.
    
    Change-Id: I601833c49f49f94e1ff3cdc994e3027ee0542b94
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147429
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/filter/ww8/ww8.cxx b/sw/qa/filter/ww8/ww8.cxx
index 611d63259ae8..8eac94ae36d0 100644
--- a/sw/qa/filter/ww8/ww8.cxx
+++ b/sw/qa/filter/ww8/ww8.cxx
@@ -15,6 +15,10 @@
 #include <docsh.hxx>
 #include <formatcontentcontrol.hxx>
 #include <wrtsh.hxx>
+#include <itabenum.hxx>
+#include <frmmgr.hxx>
+#include <frameformats.hxx>
+#include <formatflysplit.hxx>
 
 namespace
 {
@@ -189,6 +193,41 @@ CPPUNIT_TEST_FIXTURE(Test, testDocxSymbolFontExport)
     assertXPath(pXmlDoc, "//w:p/w:r/w:sym[1]", "font", "Wingdings");
     assertXPath(pXmlDoc, "//w:p/w:r/w:sym[1]", "char", "f0e0");
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testDocxFloatingTableExport)
+{
+    // Given a document with a floating table:
+    createSwDoc();
+    SwDoc* pDoc = getSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    // Insert a table:
+    SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
+    pWrtShell->InsertTable(aTableOptions, 1, 1);
+    pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
+    // Select it:
+    pWrtShell->SelAll();
+    // Wrap in a fly:
+    SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr);
+    pWrtShell->StartAllAction();
+    aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PARA, aMgr.GetPos(), aMgr.GetSize());
+    // Mark it as a floating table:
+    SwFrameFormats& rFlys = *pDoc->GetSpzFrameFormats();
+    SwFrameFormat* pFly = rFlys[0];
+    SwAttrSet aSet(pFly->GetAttrSet());
+    aSet.Put(SwFormatFlySplit(true));
+    pDoc->SetAttr(aSet, *pFly);
+    pWrtShell->EndAllAction();
+
+    // When saving to docx:
+    save("Office Open XML Text");
+
+    // Then make sure we write a floating table, not a textframe containing a 
table:
+    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - XPath '//w:tbl/w:tblPr/w:tblpPr' number of nodes is incorrect
+    // i.e. no floating table was exported.
+    assertXPath(pXmlDoc, "//w:tbl/w:tblPr/w:tblpPr", 1);
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 6d1c94868917..ff69fb0b8e2c 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -136,6 +136,7 @@
 #include <txtatr.hxx>
 #include <frameformats.hxx>
 #include <textcontentcontrol.hxx>
+#include <formatflysplit.hxx>
 
 #include <o3tl/string_view.hxx>
 #include <o3tl/unit_conversion.hxx>
@@ -408,7 +409,14 @@ static void 
checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutpu
         std::map<OUString, css::uno::Any> aTableGrabBag = 
pTableGrabBag->GetGrabBag();
         // no grabbag?
         if (aTableGrabBag.find("TablePosition") == aTableGrabBag.end())
+        {
+            if (pFrameFormat->GetFlySplit().GetValue())
+            {
+                ww8::Frame aFrame(*pFrameFormat, *rAnchor.GetContentAnchor());
+                rDocxAttributeOutput.WriteFloatingTable(&aFrame);
+            }
             continue;
+        }
 
         // write table to docx
         ww8::Frame aFrame(*pFrameFormat, *rAnchor.GetContentAnchor());
@@ -4559,6 +4567,84 @@ sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& 
rDocExport)
     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 )
@@ -4665,6 +4751,16 @@ void DocxAttributeOutput::TableDefinition( 
ww8::WW8TableNodeInfoInner::Pointer_t
     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 )
     {
@@ -4724,74 +4820,8 @@ void DocxAttributeOutput::TableDefinition( 
ww8::WW8TableNodeInfoInner::Pointer_t
             const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
             if( pFrame )
             {
-                // 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( 
pFrame->GetFrameFormat().GetHoriOrient().GetHoriOrient(), 
pFrame->GetFrameFormat().GetHoriOrient().IsPosToggle() );
-                OString sTblpYSpec = convertToOOXMLVertOrient( 
pFrame->GetFrameFormat().GetVertOrient().GetVertOrient() );
-
-                sOrientation = convertToOOXMLVertOrientRel( 
pFrame->GetFrameFormat().GetVertOrient().GetRelationOrient() );
-                attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), 
sOrientation);
-
-                if( !sTblpYSpec.isEmpty() )
-                    attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), 
sTblpYSpec);
-
-                sOrientation = convertToOOXMLHoriOrientRel( 
pFrame->GetFrameFormat().GetHoriOrient().GetRelationOrient() );
-                attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), 
sOrientation);
-
-                if( !sTblpXSpec.isEmpty() )
-                    attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), 
sTblpXSpec);
-
-                nValue = pFrame->GetFrameFormat().GetULSpace().GetLower();
-                if( nValue != 0 )
-                    attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), 
OString::number( nValue ) );
-
-                nValue = pFrame->GetFrameFormat().GetLRSpace().GetLeft();
-                if( nValue != 0 )
-                    attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), 
OString::number( nValue ) );
-
-                nValue = pFrame->GetFrameFormat().GetLRSpace().GetRight();
-                if( nValue != 0 )
-                    attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), 
OString::number( nValue ) );
-
-                nValue = pFrame->GetFrameFormat().GetULSpace().GetUpper();
-                if( nValue != 0 )
-                    attrListTablePos->add( FSNS( XML_w, XML_topFromText ), 
OString::number( nValue ) );
-
-                if( sTblpXSpec.isEmpty() ) // do not write tblpX if tblpXSpec 
is present
-                {
-                    nValue = pFrame->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(m_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);
-                    }
-
-                    attrListTablePos->add( FSNS( XML_w, XML_tblpX ), 
OString::number( nValue ) );
-                }
-
-                if( sTblpYSpec.isEmpty() ) // do not write tblpY if tblpYSpec 
is present
-                {
-                    nValue = pFrame->GetFrameFormat().GetVertOrient().GetPos();
-                    attrListTablePos->add( FSNS( XML_w, XML_tblpY ), 
OString::number( nValue ) );
-                }
+                CollectFloatingTableAttributes(m_rExport, *pFrame, 
pTableTextNodeInfoInner,
+                                               attrListTablePos);
             }
             else // ( pFrame = 0 )
             {
@@ -4851,7 +4881,10 @@ void DocxAttributeOutput::TableDefinition( 
ww8::WW8TableNodeInfoInner::Pointer_t
                 }
             }
 
-            m_pSerializer->singleElementNS( XML_w, XML_tblpPr, 
attrListTablePos);
+            if (!bFloatingTableWritten)
+            {
+                m_pSerializer->singleElementNS(XML_w, XML_tblpPr, 
attrListTablePos);
+            }
             attrListTablePos = nullptr;
         }
         else
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx 
b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index 9ecf646af705..fc19793991f8 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -59,6 +59,22 @@
 #include <utility>
 #endif
 
+namespace
+{
+bool IsFlySplitAllowed()
+{
+    bool bRet
+        = 
officecfg::Office::Writer::Filter::Import::DOCX::ImportFloatingTableAsSplitFly::get();
+
+    if (!bRet)
+    {
+        bRet = getenv("SW_FORCE_FLY_SPLIT") != nullptr;
+    }
+
+    return bRet;
+}
+}
+
 namespace writerfilter::dmapper {
 
 using namespace ::com::sun::star;
@@ -375,7 +391,10 @@ TableStyleSheetEntry * 
DomainMapperTableHandler::endTableGetTableStyle(TableInfo
                 comphelper::makePropertyValue("vertAnchor", 
pTablePositions->getVertAnchor())
             };
 
-            aGrabBag["TablePosition"] <<= aGrabBagTS;
+            if (!IsFlySplitAllowed())
+            {
+                aGrabBag["TablePosition"] <<= aGrabBagTS;
+            }
         }
         else if (bConvertToFloatingInFootnote)
         {
@@ -1565,12 +1584,7 @@ void DomainMapperTableHandler::endTable(unsigned int 
nestedTableLevel, bool bTab
                     comphelper::makePropertyValue("IsFollowingTextFlow", 
true));
             }
 
-            bool bSplitAllowed = 
officecfg::Office::Writer::Filter::Import::DOCX::ImportFloatingTableAsSplitFly::get();
-            if (!bSplitAllowed)
-            {
-                bSplitAllowed = getenv("SW_FORCE_FLY_SPLIT") != nullptr;
-            }
-            if (bSplitAllowed)
+            if (IsFlySplitAllowed())
             {
                 
aFrameProperties.push_back(comphelper::makePropertyValue("IsSplitAllowed", 
true));
             }

Reply via email to