diff options
author | László Németh <nemeth@numbertext.org> | 2021-03-26 16:28:03 +0100 |
---|---|---|
committer | László Németh <nemeth@numbertext.org> | 2021-03-28 14:49:08 +0200 |
commit | e11c51eefe8c3210cef2b5850f401ba67a401d01 (patch) | |
tree | da3c189d933082328da69654e0e8fd3ae57e9d7b | |
parent | 27377e3db2792d1bf6eb733492462701defdeffc (diff) |
and endnotes by converting them to floating tables during the
import, and removing floating at the DOCX export.
Writer core doesn't support non-floating tables in footnotes
and endnotes officially, (flowfrm:cxx: "Tables in footnotes
are not truly supported"), so their DOCX import resulted
serious problems:
– missing table paint (tdf#95806);
– table loss during saving to ODT (tdf#125877);
– table loss during copying them or their footnotes and
endnotes in the document (this resulted the regression
of the optimized footnote and endnote import: tdf#141172);
– table loss during changing the order of the chapters in
the Navigator.
Change-Id: Ife8af41936ae3ab003a3a9ad33b98c1d813e9c82
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113162
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
-rw-r--r-- | sw/qa/extras/odfexport/data/tdf95806.docx | bin | 0 -> 16907 bytes | |||
-rw-r--r-- | sw/qa/extras/odfexport/odfexport.cxx | 17 | ||||
-rw-r--r-- | sw/qa/extras/ooxmlexport/data/tdf141172.docx | bin | 0 -> 16286 bytes | |||
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport14.cxx | 8 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 5 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapperTableHandler.cxx | 61 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapperTableHandler.hxx | 4 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapper_Impl.hxx | 7 | ||||
-rw-r--r-- | writerfilter/source/dmapper/PropertyMap.cxx | 8 |
9 files changed, 101 insertions, 9 deletions
diff --git a/sw/qa/extras/odfexport/data/tdf95806.docx b/sw/qa/extras/odfexport/data/tdf95806.docx Binary files differnew file mode 100644 index 000000000000..65bfaae3e423 --- /dev/null +++ b/sw/qa/extras/odfexport/data/tdf95806.docx diff --git a/sw/qa/extras/odfexport/odfexport.cxx b/sw/qa/extras/odfexport/odfexport.cxx index 841019b3073b..f570e9f5d40a 100644 --- a/sw/qa/extras/odfexport/odfexport.cxx +++ b/sw/qa/extras/odfexport/odfexport.cxx @@ -263,6 +263,23 @@ DECLARE_ODFEXPORT_TEST(testTdf139126, "tdf139126.odt") CPPUNIT_ASSERT_EQUAL(OUString("** Expression is faulty **"), xE2->getString()); } +DECLARE_ODFEXPORT_TEST(testTdf125877, "tdf95806.docx") +{ + CPPUNIT_ASSERT_EQUAL(1, getPages()); + uno::Reference<text::XTextTablesSupplier> xSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xTables(xSupplier->getTextTables(), uno::UNO_QUERY); + uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, uno::UNO_QUERY); + + // This was 0 (lost table during ODT export in footnotes) + // Note: fix also tdf#95806: painting table layout is correct + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount()); + + // floating table: there is a frame now + uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xIndexAccess->getCount()); +} + DECLARE_ODFEXPORT_TEST(testTdf103567, "tdf103567.odt") { CPPUNIT_ASSERT_EQUAL(1, getShapes()); diff --git a/sw/qa/extras/ooxmlexport/data/tdf141172.docx b/sw/qa/extras/ooxmlexport/data/tdf141172.docx Binary files differnew file mode 100644 index 000000000000..0e1647ae27e1 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/tdf141172.docx diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx index 77fcb3fd1091..cd3ca2f39e46 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx @@ -1169,6 +1169,14 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf123757, "tdf123757.docx") assertXPath(pXml, "/w:document/w:body/w:tbl", 2); } +DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf141172, "tdf141172.docx") +{ + xmlDocUniquePtr pXml = parseExport("word/endnotes.xml"); + CPPUNIT_ASSERT(pXml); + // This was 1 (lost table during copying endnote content) + assertXPath(pXml, "/w:endnotes/w:endnote/w:tbl", 2); +} + DECLARE_OOXMLEXPORT_TEST(testContSectBreakHeaderFooter, "cont-sect-break-header-footer.docx") { // Load a document with a continuous section break on page 2. diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 4b9c72829c3b..f4688e5f6a01 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -3982,7 +3982,10 @@ void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList); } - else if (rGrabBagElement.first == "TablePosition" ) + 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> >(); diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx index fd21827211bb..55c48740f0ec 100644 --- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx +++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx @@ -32,6 +32,7 @@ #include <com/sun/star/table/XCellRange.hpp> #include <com/sun/star/text/HoriOrientation.hpp> #include <com/sun/star/text/SizeType.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> #include <com/sun/star/text/XTextField.hpp> #include <com/sun/star/text/XTextRangeCompare.hpp> #include <com/sun/star/beans/XPropertySet.hpp> @@ -339,9 +340,43 @@ void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftB rInfo.nRightBorderDistance = nActualR; } +void lcl_fillEmptyFrameProperties(std::vector<beans::PropertyValue>& rFrameProperties) +{ + // fill empty frame properties to create an invisible frame around the table: + // hide frame borders and zero inner and outer frame margins + beans::PropertyValue aValue; + aValue.Name = getPropertyName( PROP_ANCHOR_TYPE ); + aValue.Value <<= text::TextContentAnchorType_AS_CHARACTER; + rFrameProperties.push_back(aValue); + + table::BorderLine2 aEmptyBorder; + static const std::vector<std::u16string_view> aBorderNames + = { u"TopBorder", u"LeftBorder", u"BottomBorder", u"RightBorder" }; + for (size_t i = 0; i < aBorderNames.size(); ++i) + { + beans::PropertyValue aBorderValue; + aBorderValue.Name = aBorderNames[i]; + aBorderValue.Value <<= aEmptyBorder; + rFrameProperties.push_back(aBorderValue); + } + static const std::vector<std::u16string_view> aMarginNames + = { u"TopBorderDistance", u"LeftBorderDistance", + u"BottomBorderDistance", u"RightBorderDistance", + u"TopMargin", u"LeftMargin", u"BottomMargin", u"RightMargin" }; + for (size_t i = 0; i < aMarginNames.size(); ++i) + { + beans::PropertyValue aMarginValue; + aMarginValue.Name = aMarginNames[i]; + aMarginValue.Value <<= sal_Int32(10); + rFrameProperties.push_back(aMarginValue); + } +} + } -TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, std::vector<beans::PropertyValue>& rFrameProperties) +TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, + std::vector<beans::PropertyValue>& rFrameProperties, + bool bConvertToFloatingInFootnote) { // will receive the table style if any TableStyleSheetEntry* pTableStyle = nullptr; @@ -394,6 +429,11 @@ TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo aGrabBag["TablePosition"] <<= aGrabBagTS; } + else if (bConvertToFloatingInFootnote) + { + // define empty "TablePosition" to avoid export temporary floating + aGrabBag["TablePosition"] = uno::Any(); + } std::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME); if(aTableStyleVal) @@ -1375,7 +1415,15 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab (m_rDMapper_Impl.getTableManager().getCurrentTablePosition()); TableInfo aTableInfo; aTableInfo.nNestLevel = nestedTableLevel; - aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties); + + // non-floating tables need floating in footnotes and endnotes, because + // Writer core cannot handle (i.e. save in ODT, copy, edit etc.) them otherwise + bool bConvertToFloating = aFrameProperties.empty() && + nestedTableLevel <= 1 && + m_rDMapper_Impl.IsInFootOrEndnote(); + bool bFloating = !aFrameProperties.empty() || bConvertToFloating; + + aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties, bConvertToFloating); // expands to uno::Sequence< Sequence< beans::PropertyValues > > std::vector<HorizontallyMergedCell> aMerges; @@ -1392,7 +1440,8 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab uno::Reference<text::XTextRange> xStart; uno::Reference<text::XTextRange> xEnd; - bool bFloating = !aFrameProperties.empty(); + if ( bConvertToFloating ) + lcl_fillEmptyFrameProperties(aFrameProperties); // OOXML table style may contain paragraph properties, apply these on cell paragraphs if ( m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements() ) @@ -1585,7 +1634,11 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab sal_Int32 nTableWidthType = text::SizeType::FIX; m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType); if (m_rDMapper_Impl.GetSectionContext() && nestedTableLevel <= 1 && !m_rDMapper_Impl.IsInHeaderFooter()) - m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd, comphelper::containerToSequence(aFrameProperties), nTableWidth, nTableWidthType); + { + m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd, + comphelper::containerToSequence(aFrameProperties), + nTableWidth, nTableWidthType, bConvertToFloating); + } else { // m_xText points to the body text, get the current xText from m_rDMapper_Impl, in case e.g. we would be in a header. diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx index 900ff747f4cc..5a2a37acd90f 100644 --- a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx +++ b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx @@ -70,7 +70,9 @@ class DomainMapperTableHandler final : public virtual SvRefBase /// Did we have a foot or endnote in this table? bool m_bHadFootOrEndnote; - TableStyleSheetEntry * endTableGetTableStyle(TableInfo & rInfo, std::vector<css::beans::PropertyValue>& rFrameProperties); + TableStyleSheetEntry * endTableGetTableStyle(TableInfo & rInfo, + std::vector<css::beans::PropertyValue>& rFrameProperties, + bool bConvertToFloating); CellPropertyValuesSeq_t endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges); css::uno::Sequence<css::beans::PropertyValues> endTableGetRowProperties(); diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx index 6aa33913d924..1b29e96cbc27 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx @@ -395,16 +395,19 @@ struct FloatingTableInfo sal_Int32 m_nTableWidthType; /// Break type of the section that contains this table. sal_Int32 m_nBreakType = -1; + /// Tables in footnotes and endnotes are always floating + bool m_bConvertToFloatingInFootnote = false; FloatingTableInfo(css::uno::Reference<css::text::XTextRange> const& xStart, css::uno::Reference<css::text::XTextRange> const& xEnd, const css::uno::Sequence<css::beans::PropertyValue>& aFrameProperties, - sal_Int32 nTableWidth, sal_Int32 nTableWidthType) + sal_Int32 nTableWidth, sal_Int32 nTableWidthType, bool bConvertToFloatingInFootnote) : m_xStart(xStart), m_xEnd(xEnd), m_aFrameProperties(aFrameProperties), m_nTableWidth(nTableWidth), - m_nTableWidthType(nTableWidthType) + m_nTableWidthType(nTableWidthType), + m_bConvertToFloatingInFootnote(bConvertToFloatingInFootnote) { } css::uno::Any getPropertyValue(std::u16string_view propertyName); diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx index f7a8f800d7d0..cc556ddc6330 100644 --- a/writerfilter/source/dmapper/PropertyMap.cxx +++ b/writerfilter/source/dmapper/PropertyMap.cxx @@ -1138,6 +1138,9 @@ void SectionPropertyMap::HandleMarginsHeaderFooter( bool bFirstPage, DomainMappe bool SectionPropertyMap::FloatingTableConversion( const DomainMapper_Impl& rDM_Impl, FloatingTableInfo& rInfo ) { + // always convert non-floating tables to floating ones in footnotes and endnotes + if ( rInfo.m_bConvertToFloatingInFootnote ) + return true; // This is OOXML version of the code deciding if the table needs to be // in a floating frame. // For ww8 code, see SwWW8ImplReader::FloatingTableConversion in @@ -1388,12 +1391,15 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl ) // Text area width is known at the end of a section: decide if tables should be converted or not. std::vector<FloatingTableInfo>& rPendingFloatingTables = rDM_Impl.m_aPendingFloatingTables; - uno::Reference<text::XTextAppendAndConvert> xBodyText( rDM_Impl.GetBodyText(), uno::UNO_QUERY ); for ( FloatingTableInfo & rInfo : rPendingFloatingTables ) { rInfo.m_nBreakType = m_nBreakType; if ( FloatingTableConversion( rDM_Impl, rInfo ) ) { + uno::Reference<text::XTextAppendAndConvert> xBodyText( + rInfo.m_bConvertToFloatingInFootnote + ? rInfo.m_xStart->getText() + : rDM_Impl.GetBodyText(), uno::UNO_QUERY ); std::deque<css::uno::Any> aFramedRedlines = rDM_Impl.m_aStoredRedlines[StoredRedlines::FRAME]; try { |