diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2023-02-22 08:11:00 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2023-02-22 09:36:50 +0000 |
commit | e7be3b821cd42fdc9d8e51772b8202030d76497e (patch) | |
tree | 8cb11757570c267f431ec79f7ea945d302da526e | |
parent | a60fcb15046a60fa35fc4ea07d1411bf3563fe2e (diff) |
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 <vmiklos@collabora.com>
Tested-by: Jenkins
-rw-r--r-- | sw/qa/filter/ww8/ww8.cxx | 39 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 171 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapperTableHandler.cxx | 28 |
3 files changed, 162 insertions, 76 deletions
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)); } |